Dart | 在 Flutter 中使用 ValueNotifiers 和 InheritedWidgets 进行简单的状态管理

1 概述

Flutter 这几年得到迅速的发展,自支持桌面系统开发以来,已不再是纯粹的移动开发框架。然而,当我们使用 Flutter 构建应用时, 有大量的状态管理库或包可供选择,这势必会成为初学者的噩梦。

本文就从简单讲起,着重介绍 ValueNotifiersInheritedWidgets 是如何进行状态管理的。

2 从一个例子开始

我们将实现一个水果列表的收藏夹,效果如下:
在这里插入图片描述

2.1 创建 Flutter 项目

默认情况下 main.dart 代码如下:

import 'package:flutter/material.dart';


void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      debugShowCheckedModeBanner: false,
      home: MyWidget()
    );
  }
}

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Wish List'),
      ),
      body: Center(
        child: Text('Hello, World!'),
      ),
    );
  }
}

ValueNotifierChangeNotifier 的子类,它管理单个值的状态,并在值更改时通知侦听器。它与 ValueListableBuilder 小部件一起工作,自动监听 ValueListenableBuilder 上的更改。下面是 ValueNotifier 的简单应用。

class MyWidget extends StatelessWidget {

  // 创建监听器
  final ValueNotifier<int> counter = ValueNotifier<int>(0);
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Wish List'),
      ),
      body: Center(
        // 必须配合 ValueListenableBuilder 使用
        child: ValueListenableBuilder(
          valueListenable: counter,
          builder: (context, value, _) {
            return Text('${counter.value}');
          }
        ),
      ),
      floatingActionButton:  FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () {
          counter.value += 1;
          print(counter.value);
        }
      )  
    );
  }
}

2.2 自定义 ValueNotifier

我们可以通过继承 ValueNotifier 类,来扩展功能。

// 在构造函数里,添加需要监听的值
class FavoriteNotifier extends ValueNotifier<List<String>> {
  FavoriteNotifier(List<String> value) : super(value);
}

接下来,我们增加收藏夹的添加和删除方法。

class FavoriteNotifier extends ValueNotifier<List<String>> {
  FavoriteNotifier(List<String> value) : super(value);
  
  void toggleFavorite(String item) {
    if (!value.contains(item)) {
      value.add(item);
    } else {
      value.remove(item);
    }
    
    notifyListeners();
  }
  
  void clearFavorites() {
    value.clear();
    notifyListeners();
  }
}

注意:我们在每个方法中都使用了 notifyListener 方法。如果没有它,当值更改或更新时,侦听器将不会得到通知。

那么问题来了,如何将自定义的 FavoriteNotifier 类,传给部件呢?这里就要使用 InheritedWidgets,不过要首先更新先 UI 代码。


class MyWidget extends StatelessWidget {
  final List<String> fruits = ['Mango', 'Pear', 'Cashew',
    'Grape', 'Guava', 'Coconut', 'Orange'];
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Favorite Fruits'),
        actions: [
          Icon(Icons.favorite),
          FavoriteCount(),
        ]
      ),
      body: ListView.separated(
        itemCount: fruits.length,
        separatorBuilder: (context, _) => Divider(),
        itemBuilder: (context, index) {
          final fruit = fruits[index];
          return ListTile(
            leading: Icon(Icons.local_florist),
            title: Text(fruit),
            onTap: () {},
          );
        }
      ),
      floatingActionButton:  FloatingActionButton(
        child: Icon(Icons.clear),
        onPressed: () {
        }
      )  
    );
  }
}

class FavoriteCount extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Center(
        child: Text("0"),
      ),
    );
  }
}

2.4 使用 InheritedWidgets

InheritedWidgets 允许我们接受来自小部件祖先的数据,不必再向下传递。

class FavoriteState extends InheritedWidget{
  final FavoriteNotifier favoriteNotifier;
  
  FavoriteState({required this.favoriteNotifier, required Widget child})
      : super(child: child);

  // 调用子部件
  static FavoriteState of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType()!;
  }

  @override
  bool updateShouldNotify(FavoriteState oldWidget) {
    return oldWidget.favoriteNotifier.value != favoriteNotifier.value;
  }
}

接下来,就要使用 FavoriteState 来包装 MaterialsApp

class MyApp extends StatelessWidget {
  final List<String> favoriteList = [];
  @override
  Widget build(BuildContext context) {
    return FavoriteState(
      favoriteNotifier: FavoriteNotifier(favoriteList),
      child: MaterialApp(
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        debugShowCheckedModeBanner: false,
        home: MyWidget()
      ),
    );
  }
}

最后,再使用 ValueListenableBuilder 包装 FavoriteNotifier 接受数据更新,通知 UI 界面刷新。

class MyWidget extends StatelessWidget {
  final List<String> fruits = ['Mango', 'Pear', 'Cashew',
    'Grape', 'Guava', 'Coconut', 'Orange'];
  
  @override
  Widget build(BuildContext context) {
    final favoriteNotifier = FavoriteState.of(context).favoriteNotifier;
    
    return Scaffold(
      appBar: AppBar(
        title: Text('Favorite Fruits'),
        actions: [
          Icon(Icons.favorite),
          FavoriteCount(),
        ]
      ),
      body: ListView.separated(
        itemCount: fruits.length,
        separatorBuilder: (context, _) => Divider(),
        itemBuilder: (context, index) {
          final fruit = fruits[index];
          return ValueListenableBuilder(
            valueListenable: favoriteNotifier,
            builder: (context, List<String> value, _) {
              return ListTile(
                leading: Icon(Icons.local_florist, color: value.contains(fruit) 
                              ? Colors.orange 
                              : Colors.grey[600]),
                title: Text(fruit),
                onTap: () {
                  favoriteNotifier.toggleFavorite(fruit);
                },
              );
            }
          );
        }
      ),
      floatingActionButton:  FloatingActionButton(
        child: Icon(Icons.clear),
        onPressed: () {
          favoriteNotifier.clearFavorites();
        }
      ),  
    );
  }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孟华328

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值