Flutter Riverpod上手指南

什么是 Riverpod?

Riverpod 是由 Flutter 狀態(tài)管理專家 Remi Rousselet 開發(fā)的狀態(tài)管理庫,是 Provider 的繼任者。它解決了 Provider 的許多限制,提供了更好的開發(fā)體驗和更強大的功能:

  • 完全避免了ProviderNotFoundException
  • 支持自動代碼生成,減少模板代碼
  • 更好的類型安全性
  • 更簡單的測試
  • 輕松組合狀態(tài)
  • 自動緩存和重新計算

一、環(huán)境搭建

1. 安裝依賴

pubspec.yaml中添加以下依賴:

dependencies:
  flutter:
    sdk: flutter
  flutter_riverpod: ^3.0.0
  riverpod_annotation: ^3.0.0

dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner: ^2.7.1
  riverpod_generator: ^3.0.0
  custom_lint: ^0.8.0
  riverpod_lint: ^3.0.0

然后運行flutter pub get安裝依賴。

2. 配置入口

Riverpod 需要在應(yīng)用的根部包裹ProviderScope

import 'package:demo_tester/pages/home_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(
    // 為整個應(yīng)用提供Riverpod支持
    ProviderScope(
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}


二、基礎(chǔ)概念與第一個 Provider

1. 定義一個簡單的 Provider

// 定義一個簡單的Provider
final helloWorldProvider = Provider((ref) {
  return "Hello, Riverpod!";
});

2. 消費 Provider

使用ConsumerWidget(替代StatelessWidget)來消費 Provider:

import 'package:demo_tester/pages/combined_provider_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class HomePage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 監(jiān)聽helloWorldProvider并獲取其值
    final String message = ref.watch(helloWorldProvider);
    
    return Scaffold(
      appBar: AppBar(title: Text("Riverpod Demo")),
      body: Center(
        child: Text(message),
      ),
    );
  }
}


三、常用 Provider 類型

1. StateProvider - 簡單狀態(tài)管理

用于管理簡單的狀態(tài),適合存儲布爾值、數(shù)字、字符串等簡單數(shù)據(jù):

// 定義一個計數(shù)器的StateProvider
final counterProvider = StateProvider((ref) => 0);

使用它:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_riverpod/legacy.dart';

class CounterPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 監(jiān)聽狀態(tài)變化
    final count = ref.watch(counterProvider);

    return Scaffold(
      appBar: AppBar(title: Text("Counter")),
      body: Center(child: Text("Count: $count")),
      floatingActionButton: FloatingActionButton(
        // 更新狀態(tài)
        onPressed: () => ref.read(counterProvider.notifier).state++,
        child: Icon(Icons.add),
      ),
    );
  }
}

2. StateNotifierProvider - 復(fù)雜狀態(tài)管理

當(dāng)需要更復(fù)雜的狀態(tài)邏輯時,使用StateNotifierProvider

首先創(chuàng)建狀態(tài)類和 StateNotifier:

// 定義狀態(tài)類
class CounterState {
  final int count;
  CounterState({required this.count});
  
  // 復(fù)制方法,用于創(chuàng)建新狀態(tài)
  CounterState copyWith({int? count}) {
    return CounterState(count: count ?? this.count);
  }
}

// 定義StateNotifier
class CounterNotifier extends StateNotifier<CounterState> {
  // 初始化狀態(tài)
  CounterNotifier() : super(CounterState(count: 0));
  
  // 增加計數(shù)的方法
  void increment() {
    state = state.copyWith(count: state.count + 1);
  }
  
  // 減少計數(shù)的方法
  void decrement() {
    state = state.copyWith(count: state.count - 1);
  }
  
  // 重置計數(shù)
  void reset() {
    state = CounterState(count: 0);
  }
}

// 創(chuàng)建Provider
final counterNotifierProvider = StateNotifierProvider<CounterNotifier, CounterState>((ref) {
  return CounterNotifier();
});

使用它:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_riverpod/legacy.dart';

class AdvancedCounterPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 監(jiān)聽狀態(tài)
    final counterState = ref.watch(counterNotifierProvider);
    // 獲取notifier以調(diào)用方法
    final counterNotifier = ref.read(counterNotifierProvider.notifier);

    return Scaffold(
      appBar: AppBar(title: Text("Advanced Counter")),
      body: Center(child: Text("Count: ${counterState.count}")),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: () => counterNotifier.decrement(),
            child: Icon(Icons.remove),
          ),
          SizedBox(width: 10),
          FloatingActionButton(
            onPressed: () => counterNotifier.reset(),
            child: Icon(Icons.refresh),
          ),
          SizedBox(width: 10),
          FloatingActionButton(
            onPressed: () => counterNotifier.increment(),
            child: Icon(Icons.add),
          ),
        ],
      ),
    );
  }
}

3. FutureProvider - 處理異步數(shù)據(jù)

用于處理異步操作,如 API 請求:

// 模擬API請求
Future<String> fetchUserData() async {
  await Future.delayed(Duration(seconds: 2));
  return "John Doe";
}

// 創(chuàng)建FutureProvider
final userProvider = FutureProvider((ref) async {
  return fetchUserData();
});

使用它:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class UserPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 監(jiān)聽FutureProvider
    final userAsyncValue = ref.watch(userProvider);

    return Scaffold(
      appBar: AppBar(title: Text("User Data")),
      body: Center(
        child: userAsyncValue.when(
          loading: () => CircularProgressIndicator(),
          error: (error, stack) => Text("Error: $error"),
          data: (user) => Text("User: $user"),
        ),
      ),
    );
  }
}

4. StreamProvider - 處理流數(shù)據(jù)

用于處理 Stream,如實時數(shù)據(jù)更新:

// 創(chuàng)建一個簡單的計數(shù)器流
Stream<int> countStream() {
  return Stream.periodic(Duration(seconds: 1), (count) => count);
}

// 創(chuàng)建StreamProvider
final streamProvider = StreamProvider((ref) {
  return countStream();
});

使用它:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class StreamPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final streamAsyncValue = ref.watch(streamProvider);

    return Scaffold(
      appBar: AppBar(title: Text("Stream Demo")),
      body: Center(
        child: streamAsyncValue.when(
          loading: () => CircularProgressIndicator(),
          error: (error, stack) => Text("Error: $error"),
          data: (count) => Text("Seconds passed: $count"),
        ),
      ),
    );
  }
}


四、Provider 組合

Riverpod 的一大優(yōu)勢是可以輕松組合多個 Provider:

// 定義兩個簡單的Provider
final nameProvider = Provider((ref) => "John");
final ageProvider = Provider((ref) => 30);

// 組合上面兩個Provider
final userInfoProvider = Provider((ref) {
  final name = ref.watch(nameProvider);
  final age = ref.watch(ageProvider);
  return "Name: $name, Age: $age";
});

使用組合后的 Provider:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class CombinedProviderPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final userInfo = ref.watch(userInfoProvider);

    return Scaffold(
      appBar: AppBar(title: Text("Combined Providers")),
      body: Center(child: Text(userInfo)),
    );
  }
}


五、自動代碼生成

使用riverpod_generator可以減少模板代碼,提高開發(fā)效率。

1. 創(chuàng)建一個使用代碼生成的 Provider

首先創(chuàng)建一個文件user_provider.dart

import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'user_provider.g.dart';

@riverpod
String userName(Ref ref) {
  return "Alice";
}

@riverpod
int userAge(Ref ref) {
  return 25;
}

@riverpod
String userDetails(Ref ref) {
  final name = ref.watch(userNameProvider);
  final age = ref.watch(userAgeProvider);
  return "$name is $age years old";
}

2. 運行代碼生成

在終端運行:

flutter packages pub run build_runner build --delete-conflicting-outputs

這會生成user_provider.g.dart文件,包含生成的代碼。

3. 使用生成的 Provider

import 'package:demo_tester/provider/user_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class GeneratedProviderPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final details = ref.watch(userDetailsProvider);

    return Scaffold(
      appBar: AppBar(title: Text("Generated Providers")),
      body: Center(
        child: Text(details),
      ),
    );
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容