在应用开发过程中数据传递,flutter提供 InheritedWidget 以及多种 provider, 各有差异从从使用习惯上面 这边主要介绍以下两种:
- InheritedWidget
- provider (ChangeNotifier)
InheritedWidget:
提供一种 从上而下 的数据提供 (而且子节点需要 Widget 包裹); 且单向。切记只能由上而下的刷新数据;不能做到 子节点 变数据 而刷新的情况!常用于 静态数据的存储;不会改变的常量保存; 或者子节点只作为 静态展示的数据,父节点提供元数据。子节点展示数据 不参与动态更新交互
效果如下:
脚本结构:
inherited_datum.dart
import 'package:flutter/cupertino.dart';
class InheritedDatum extends InheritedWidget {
final int count;
const InheritedDatum({Key? key, required child, required this.count})
: super(key: key, child: child);
static InheritedDatum? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<InheritedDatum>();
}
///**子组件不能 通过调用该方法 实现子组件刷新
updateCount(int count) {
count = count;
}
@override
bool updateShouldNotify(covariant InheritedDatum oldWidget) {
// TODO: implement updateShouldNotify
return oldWidget.count != count;
}
}
widget_child0.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'inherited_datum.dart';
class WidgetChild0 extends StatelessWidget {
const WidgetChild0({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
height: 40,
width: 80,
child: Center(
child: Text(
'${InheritedDatum.of(context)?.count}',
style: TextStyle(color: Colors.white),
),
),
);
}
}
widget_child1.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'inherited_datum.dart';
class WidgetChild1 extends StatelessWidget {
const WidgetChild1({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
height: 40,
width: 80,
child: Center(
child: Text(
'${InheritedDatum.of(context)?.count}',
style: TextStyle(color: Colors.white),
),
),
);
}
}
入口:main.dart
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
child: InheritedDatum(
count: _counter,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
// children: const [],
children: const [WidgetChild0(), WidgetChild1()],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
InheritedWidget 使用注意细节:
1. 必须是 子组件:平行的都不行:
2. 子组件获取 InheritedWidget 中的数据 通过:
InheritedDatum.of(context)?.count
3. 子组件不能仅 通过调用 InheritedWidget 中的方便实现 子组件刷新
小结:
InheritedWidget 的适用场景:子组件共享 父组件中的数据;但不具备 修改更新 父组件中数据的 能力!下面介绍的 ChangeNotifier 则具备 上下 更新数据的能力
provider 数据传递
provider是基于InheritedWidget 的包装 ;为了在Provider中进行数据共享首先我们需要为其定义一个数据模型,为了能够订阅数据的状态通常会让这个数据模型来继承
ChangeNotifier,达到更新的目的; 使用前先导入:
provider: ^6.0.5dependencies: flutter: sdk: flutter provider: ^6.0.5
之前的总结:
需要在父节点中使用以下 组件来完成 子组件的更新:按照自己 更新逻辑的 复杂度。按照实际需要选取适合的 模型
- ChangeNotifierProvider
- ProxyProvider
- MultiProvider
- Consumer
效果如下:
父组件 和 子组件都可以 实现对数据的更新
脚本结构:counter.dart
import 'package:flutter/cupertino.dart';
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
void set(int count) {
_count = count;
notifyListeners();
}
}
widget_child0.dart
import 'package:untitled1/counter.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class WidgetChild0 extends StatelessWidget {
const WidgetChild0({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
height: 40,
width: 80,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
context.read<Counter>().set(0);
print('WidgetChild0');
},
child: Center(
child: Text(
'${context.watch<Counter>().count}',
style: TextStyle(color: Colors.white),
),
),
),
);
}
}
widget_child1.dart
import 'package:untitled1/counter.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class WidgetChild1 extends StatelessWidget {
const WidgetChild1({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
height: 40,
width: 80,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
context.read<Counter>().set(1);
print('WidgetChild1');
},
child: Center(
child: Text(
'${context.watch<Counter>().count}',
style: TextStyle(color: Colors.white),
),
),
),
);
}
}
main.dart
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ChangeNotifierProvider(
create: (_) => Counter(),
child: const MyHomePage(title: 'Flutter Demo Home Page'),
),
//home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
// children: const [],
children: const [WidgetChild0(), WidgetChild1()],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.read<Counter>().increment();
},
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
provide 使用注意细节:
也是共享的数据需要放在 父节点中; 再共有的父节点中 都可以对 数据进行修改更新; 实现双向动态数据: 调用
notifyListeners
其中 MultiProvider,Consumer 适合多组 ChangeNotifier ;以实现解耦操作;基本用法和ChangeNotifierProvider 相同;