<返回目录     Powered by claud/xia兄

第8课: 状态管理

setState、Provider、Riverpod

状态管理概述

状态管理是Flutter应用开发的核心概念,用于管理应用中的数据和UI更新。

setState - 局部状态管理

class CounterWidget extends StatefulWidget {
  @override
  State createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State {
  int _counter = 0;

  void _increment() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('计数: $_counter'),
        ElevatedButton(
          onPressed: _increment,
          child: Text('增加'),
        ),
      ],
    );
  }
}

InheritedWidget - 数据向下传递

// 定义InheritedWidget
class CounterProvider extends InheritedWidget {
  final int counter;
  final Function() increment;

  const CounterProvider({
    Key? key,
    required this.counter,
    required this.increment,
    required Widget child,
  }) : super(key: key, child: child);

  static CounterProvider? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType();
  }

  @override
  bool updateShouldNotify(CounterProvider oldWidget) {
    return counter != oldWidget.counter;
  }
}

// 使用
class MyApp extends StatefulWidget {
  @override
  State createState() => _MyAppState();
}

class _MyAppState extends State {
  int _counter = 0;

  void _increment() {
    setState(() => _counter++);
  }

  @override
  Widget build(BuildContext context) {
    return CounterProvider(
      counter: _counter,
      increment: _increment,
      child: MaterialApp(
        home: HomePage(),
      ),
    );
  }
}

// 子Widget访问数据
class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final provider = CounterProvider.of(context)!;
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('计数: ${provider.counter}'),
            ElevatedButton(
              onPressed: provider.increment,
              child: Text('增加'),
            ),
          ],
        ),
      ),
    );
  }
}

Provider - 推荐的状态管理方案

1. 安装Provider

# pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  provider: ^6.0.0

2. 创建数据模型

import 'package:flutter/foundation.dart';

class CounterModel extends ChangeNotifier {
  int _counter = 0;

  int get counter => _counter;

  void increment() {
    _counter++;
    notifyListeners();  // 通知监听者更新
  }

  void decrement() {
    _counter--;
    notifyListeners();
  }

  void reset() {
    _counter = 0;
    notifyListeners();
  }
}

3. 提供Provider

import 'package:provider/provider.dart';

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CounterModel(),
      child: MyApp(),
    ),
  );
}

// 多个Provider
void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => CounterModel()),
        ChangeNotifierProvider(create: (context) => UserModel()),
        ChangeNotifierProvider(create: (context) => CartModel()),
      ],
      child: MyApp(),
    ),
  );
}

4. 消费Provider

// 方法1: Consumer
class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Consumer(
              builder: (context, counter, child) {
                return Text(
                  '计数: ${counter.counter}',
                  style: TextStyle(fontSize: 32),
                );
              },
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                context.read().increment();
              },
              child: Text('增加'),
            ),
          ],
        ),
      ),
    );
  }
}

// 方法2: Provider.of
class CounterDisplay extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = Provider.of(context);
    return Text('计数: ${counter.counter}');
  }
}

// 方法3: context扩展方法
class CounterButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () {
        context.read().increment();
      },
      child: Text('增加'),
    );
  }
}

购物车示例

// 商品模型
class Product {
  final String id;
  final String name;
  final double price;

  Product({required this.id, required this.name, required this.price});
}

// 购物车模型
class CartModel extends ChangeNotifier {
  final List _items = [];

  List get items => _items;

  int get itemCount => _items.length;

  double get totalPrice {
    return _items.fold(0, (sum, item) => sum + item.price);
  }

  void addItem(Product product) {
    _items.add(product);
    notifyListeners();
  }

  void removeItem(String productId) {
    _items.removeWhere((item) => item.id == productId);
    notifyListeners();
  }

  void clear() {
    _items.clear();
    notifyListeners();
  }
}

// 使用购物车
class ProductListPage extends StatelessWidget {
  final List products = [
    Product(id: '1', name: '商品1', price: 99.9),
    Product(id: '2', name: '商品2', price: 199.9),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('商品列表'),
        actions: [
          Consumer(
            builder: (context, cart, child) {
              return Stack(
                children: [
                  IconButton(
                    icon: Icon(Icons.shopping_cart),
                    onPressed: () {
                      Navigator.push(
                        context,
                        MaterialPageRoute(builder: (context) => CartPage()),
                      );
                    },
                  ),
                  if (cart.itemCount > 0)
                    Positioned(
                      right: 8,
                      top: 8,
                      child: Container(
                        padding: EdgeInsets.all(2),
                        decoration: BoxDecoration(
                          color: Colors.red,
                          borderRadius: BorderRadius.circular(10),
                        ),
                        constraints: BoxConstraints(
                          minWidth: 16,
                          minHeight: 16,
                        ),
                        child: Text(
                          '${cart.itemCount}',
                          style: TextStyle(
                            color: Colors.white,
                            fontSize: 10,
                          ),
                          textAlign: TextAlign.center,
                        ),
                      ),
                    ),
                ],
              );
            },
          ),
        ],
      ),
      body: ListView.builder(
        itemCount: products.length,
        itemBuilder: (context, index) {
          final product = products[index];
          return ListTile(
            title: Text(product.name),
            subtitle: Text('¥${product.price}'),
            trailing: ElevatedButton(
              onPressed: () {
                context.read().addItem(product);
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(content: Text('已添加到购物车')),
                );
              },
              child: Text('加入购物车'),
            ),
          );
        },
      ),
    );
  }
}

Riverpod - 新一代状态管理

1. 安装Riverpod

# pubspec.yaml
dependencies:
  flutter_riverpod: ^2.0.0

2. 定义Provider

import 'package:flutter_riverpod/flutter_riverpod.dart';

// StateProvider - 简单状态
final counterProvider = StateProvider((ref) => 0);

// StateNotifierProvider - 复杂状态
class CounterNotifier extends StateNotifier {
  CounterNotifier() : super(0);

  void increment() => state++;
  void decrement() => state--;
  void reset() => state = 0;
}

final counterNotifierProvider = StateNotifierProvider(
  (ref) => CounterNotifier(),
);

3. 使用Riverpod

void main() {
  runApp(
    ProviderScope(
      child: MyApp(),
    ),
  );
}

class HomePage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final counter = ref.watch(counterProvider);

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('计数: $counter', style: TextStyle(fontSize: 32)),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                ref.read(counterProvider.notifier).state++;
              },
              child: Text('增加'),
            ),
          ],
        ),
      ),
    );
  }
}
状态管理方案选择

练习任务

  1. 使用setState创建一个待办事项列表
  2. 使用Provider实现一个主题切换功能(亮色/暗色)
  3. 创建一个购物车应用,使用Provider管理商品和购物车状态
  4. 实现一个用户登录状态管理,登录后在多个页面显示用户信息
  5. 使用Riverpod创建一个计数器应用