Try   HackMD

Riverpod 簡介

網路上中文資源有點少,只好看影片整理起來當 cheat sheet 用。

# Provider 類型

Provider

型別簡單、值不可被修改的 provider 。

final valueProvider = Provider<int>((ref) {
  return 0;
});

State Provider

型別簡單、值可被修改的 provider 。

final counterStateProvider = StateProvider<int>((ref) {
  return 0;
});

State Notifier Provider

型別複雜、值的修改方法可以自定義的 provider 。

class Clock extends StateNotifier<DateTime> {
  Clock(): super(DateTime.now()) {
    _timer = Timer.periodic(Duration(seconds: 1), (_) {
      state = DateTime.now();
    })
  }

  late final Timer _timer;

  @override
  void dispose() {
    _timer.cancel();
    super.dispose();
  }
}

final clockProvider = StateNotifierProvider<Clock>((ref) {
  return Clock();
});

Future Provider

當你的狀態來自於非同步的資料且你需要針對不同的狀態(loading, error, data) 加以區分時,這個 provider 幫你省去了一些麻煩。

class Book {
  final String title;
  final String author;

  Record({
    required this.title,
    required this.author,
  });
}

final bookShelfProvider = FutureProvider<List<Book>>((ref) {
  return Future.value([
    Book(
      title: 'widget tree sucks',
      author: 'lilybon',
    ),
    Book(
      title: 'use google to pass google interview, brilliant!',
      author: 'google',
    ),
  ]);
});

Stream Provider

final streamProvider = StreamProvider<int>((ref) {
  return Stream.fromIterable([0, 1]);
});

# Provider 後綴

.autoDispose

未完成

.family

未完成

# ProviderObserver

未完成

# 範例

以下範例用到的 provider 都可以從上面 Provider 類別介紹找到,就不再複製貼上了。

Example1: 計數器 (StateProvider)

ConsumerWidget - 無狀態組件要用到 riverpod 的狀態管理時需繼承該類別。 ProviderListener - 適合監聽簡單的 state provider,若組件的業務邏輯過複雜則不適用。

class Counter extends ConsumerWidget {
  @override
  Widget build (BuildContext context, ScopedReader watch) {
    final counter = watch(counterStateProvider);
    return ProviderListener<StateController<int>>(
      provider: counterStateProvider,
      onChange: (context, counterState) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('Value is ${counterState.state}')),
        );
      },
      child: Scaffold(
        body: Center(
          child: Text(
            'Value: ${counter.state}',
            style: Theme.of(context).textTheme.headline4,
          ),
        ),
        floatActionButton: FloatActionButton(
          onPressed: () => context.read(counterStateProvider).state++
          child: Icon(Icons.add),
        )
      ),
    );
  }
}

Example2: 計時器 (StateNotifierProvider)

class Timer extends ConsumerWidget {
  @override
  Widget build (BuildContext context, ScopedReader watch) {
    final currentTime = watch(clockProvider.state);
    final timeFormatted = DateFormat.Hms().format(currentTime);
    return Scaffold(
      body: Center(
        child: Text(
          timeFormatted,
          style: Theme.of(context).textTheme.headline4,
        ),
      ),
    );
  }
}

Example3: 書櫃 (FutureProvider)

class BookShelf extends ConsumerWidget {
  @override
  Widget build (BuildContext context, ScopedReader watch) {
    final bookShelf = watch(bookShelfProvider);
    return Scaffold(
      body: Center(
        // 可針對 future 的狀態作不同的渲染
        child: bookShelf.when(
          data: (books) => Column(
            children: books.map((book) => Row(
              children: [
                Text(book.title),
                Text(book.author),
              ],
            )).toList()
          ),
          loading: () => CircularProgressIndicator(),
          error: (e, st) => Text('Error: $e'),
        ),
      ),
    );
  }
}

# 測試

環境設定

未完成

模擬及取代依賴

未完成

# 參考

tags: Flutter Riverpod