Flutter从入门到实战
一共分为23个系列
①(Flutter、Dart环境搭建篇) 共3个内容 已更新
②(Dart语法1 篇) 共4个内容 已更新
③(Dart语法2 篇) 共2个内容 已更新
④(Flutter案例开发篇) 共4个内容 已更新
⑤(Flutter的StatelessWidget 共3个内容 已更新
⑥(Flutter的基础Widget篇) 共2个内容 已更新
⑦(布局Widget篇) 共1个内容 已更新
⑧(Flex、Row、Column以及Flexible、Stack篇) 共1个内容 已更新
⑨(滚动的Widget篇) 共4个内容 已更新
⑩(Dart的Future和网络篇) 共3个内容 已更新
⑪(豆瓣案例-1篇) 共3个内容 已更新
⑫(豆瓣案例-2篇) 共3个内容 已更新
⑬(Flutter渲染流程篇) 共3个内容 已更新
⑭(状态管理篇) 共3个内容 已更新
官方文档说明
官方视频教程
Flutter的YouTube视频教程-小部件
①、状态State
1. 为什么需要状态管理
State官方文档说明
StateFulWidget
StateFulWidget视频详细说明
StatelessWidget
StatelessWidget视频详细说明
setState文档
在编写一个应用的过程中,我们有大量的State需要来进行管理,而正是对这些State的改变,来更新界面的刷新:
2. State的分类
1.1 短时状态
比如简单计数器counter
比如PagerView组件记录当前页面
比如BottomNavigationBar被选择的tab
1.2 应用AppState状态
比如用户一个个性化选项
比如用户登录状态
电商应用的后误差
新闻应用的已读 或者未读
3.共享状态管理InheritedWidget
InheritedWidget官方文档
作用:能获取最顶层的共享状态
3.1 共享状态管理InheritedWidget
的使用
页面有两个Widget 想获取一个常量进行赋值
可以这两个Widget是在同一层层级上的。
可以 继承于 InheritedWidget的类 包裹这两个Widget
然后两个widget可以获取上层的变量
main.dart
import 'dart:math';
import 'package:flutter/material.dart'; // runApp在这个material库里面
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: YHHomePage(),
);
}
}
class YHHomePage extends StatelessWidget {
const YHHomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("InheritedWidget"),
),
// 如果想共享状态 那么需要在最顶层包裹一个继承与 InheritedWidget widget即可
body: YHCounterWidget(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
YHShowData01(),
YHShowData02(),
],
),
),
),
);
}
}
class YHShowData01 extends StatelessWidget {
const YHShowData01({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
int? counter = YHCounterWidget.of(context)?.counter; // 获取最顶层的counter
return Card(
color: Colors.red,
child:Text("当前计数 : ${counter}",style: TextStyle(fontSize: 30)),
);
}
}
class YHShowData02 extends StatelessWidget {
const YHShowData02({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
int? counter = YHCounterWidget.of(context)?.counter; // 获取最顶层的counter
return Container(
color: Colors.blue,
child:Text("当前计数 : ${counter}",style: TextStyle(fontSize: 30),),
);
}
}
// 共享状态管理
class YHCounterWidget extends InheritedWidget {
final int counter = 100;
static YHCounterWidget? of(BuildContext context)
{
// 沿着Element树,去找到最近的YHCounterElement,从Element中取出Widget对象
return context.dependOnInheritedWidgetOfExactType();
}
YHCounterWidget({required Widget child}) : super(child: child);
@override
bool updateShouldNotify(covariant InheritedWidget oldWidget) {
return true;
}
}
3.1 效果图 - 共享状态管理InheritedWidget
的使用
3.2 通过点击事件改变InheritedWidget
内部的属性
如何让继承于 InheritedWidget 的类 通过外部点击按钮改变内部的值
- 可以在构造函数里面 添加参数
import 'dart:math';
import 'package:flutter/material.dart'; // runApp在这个material库里面
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: YHHomePage(),
);
}
}
class YHHomePage extends StatefulWidget {
const YHHomePage({Key? key}) : super(key: key);
@override
State<YHHomePage> createState() => _YHHomePageState();
}
class _YHHomePageState extends State<YHHomePage> {
int _counter = 100;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("InheritedWidget"),
),
// 如果想共享状态 那么需要在最顶层包裹一个继承与 InheritedWidget widget即可
body: YHCounterWidget(
counter:_counter,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
YHShowData01(),
YHShowData02(),
],
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: (){
setState(() {
_counter++;
});
},
),
);
}
}
class YHShowData01 extends StatelessWidget {
const YHShowData01({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
int? counter = YHCounterWidget.of(context)?.counter; // 获取最顶层的counter
return Card(
color: Colors.red,
child:Text("当前计数 : ${counter}",style: TextStyle(fontSize: 30)),
);
}
}
class YHShowData02 extends StatelessWidget {
const YHShowData02({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
int? counter = YHCounterWidget.of(context)?.counter; // 获取最顶层的counter
return Container(
color: Colors.blue,
child:Text("当前计数 : ${counter}",style: TextStyle(fontSize: 30),),
);
}
}
// 共享状态管理
// InheritedWidget的属性是不能够改变的
class YHCounterWidget extends InheritedWidget {
final int counter;
static YHCounterWidget? of(BuildContext context)
{
// 沿着Element树,去找到最近的YHCounterElement,从Element中取出Widget对象
return context.dependOnInheritedWidgetOfExactType();
}
YHCounterWidget({required this.counter, required Widget child}) : super(child: child);
@override
bool updateShouldNotify(covariant InheritedWidget oldWidget) {
return true;
}
}
3.2 效果图-通过点击事件改变InheritedWidget
内部的属性
4.updateShouldNotify的作用 更新时是否要通知
还记得我们之前说过的
StatefulWidget
的didChangeDependencies
函数吧
我们如果设置updateShouldNotify 为false的时候
StatefulWidget
的didChangeDependencies
不会执行的
我们如果设置updateShouldNotify 为true的时候
StatefulWidget
的didChangeDependencies
会执行的
我们可以根据旧的值是否发生改变来通知
import 'dart:math';
import 'package:flutter/material.dart'; // runApp在这个material库里面
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: YHHomePage(),
);
}
}
class YHHomePage extends StatefulWidget {
const YHHomePage({Key? key}) : super(key: key);
@override
State<YHHomePage> createState() => _YHHomePageState();
}
class _YHHomePageState extends State<YHHomePage> {
int _counter = 100;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("InheritedWidget"),
),
// 如果想共享状态 那么需要在最顶层包裹一个继承与 InheritedWidget widget即可
body: YHCounterWidget(
counter:_counter,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
YHShowData01(),
YHShowData02(),
],
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: (){
setState(() {
_counter++;
});
},
),
);
}
}
class YHShowData01 extends StatefulWidget {
const YHShowData01({Key? key}) : super(key: key);
@override
State<YHShowData01> createState() => _YHShowData01State();
}
class _YHShowData01State extends State<YHShowData01> {
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
super.didChangeDependencies();
print("执行了 didChangeDependencies");
}
@override
Widget build(BuildContext context) {
int? counter = YHCounterWidget.of(context)?.counter; // 获取最顶层的counter
return Card(
color: Colors.red,
child:Text("当前计数 : ${counter}",style: TextStyle(fontSize: 30)),
);
}
}
class YHShowData02 extends StatelessWidget {
const YHShowData02({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
int? counter = YHCounterWidget.of(context)?.counter; // 获取最顶层的counter
return Container(
color: Colors.blue,
child:Text("当前计数 : ${counter}",style: TextStyle(fontSize: 30),),
);
}
}
// 共享状态管理
// InheritedWidget的属性是不能够改变的
class YHCounterWidget extends InheritedWidget {
// 1.共享数据
final int counter;
// 2. 定义构造方法
YHCounterWidget({required this.counter, required Widget child}) : super(child: child);
// 3.获取组件最近的InheritedWidget
static YHCounterWidget? of(BuildContext context)
{
// 沿着Element树,去找到最近的YHCounterElement,从Element中取出Widget对象
return context.dependOnInheritedWidgetOfExactType();
}
// 4. 决定要不要回调State中的didChangeDependencies
// 如果返回true:执行依赖当前的InheritedWidget的State中的didChangeDependencies
@override
bool updateShouldNotify(covariant YHCounterWidget oldWidget) {
return oldWidget.counter != counter;
}
}
②、Provider
Provider是目前官方推荐的全局状态管理工具,由社区作者Remi Rousselet 和 Flutter Team共同编写。
通过 pub.dev 第三方库进行获取安装指令
进入终端使用pub get
来安装
1.案例使用 - 在App最顶层包含Provider
void main (){
runApp(
//
ChangeNotifierProvider(
create: (cxt) =>YHCounterViewModel(),
child:MyApp() ,)
);
}
ViewModel
// viewmodel
import 'package:flutter/material.dart';
// 一般都继承于 ChangeNotifier
class YHCounterViewModel extends ChangeNotifier {
int _counter = 100;
int get counter => _counter;
set counter(int value) {
_counter = value;
notifyListeners();// 通知所有的监听者 变量发生了改变
}
}
1.1 使用 Provider.of 获取ViewModel的数据 会重新调用build
class YHShowData01 extends StatelessWidget {
const YHShowData01({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
int? counter = Provider.of<YHCounterViewModel>(context).counter; // 获取Provider的属性
return Container(
color: Colors.blue,
child:Text("当前计数 : ${counter}",style: TextStyle(fontSize: 30),),
);
}
}
1.2.使用 Consumer<XXXXViewModel>(builder)
获取ViewModel的数据 (开发常用) 不会重新调用build
class YHShowData02 extends StatelessWidget {
const YHShowData02({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// int? counter = Provider.of<YHCounterViewModel>(context).counter; // 获取Provider的属性
print("YHShowData02 build");
return Container(
color: Colors.red,
child: Consumer<YHCounterViewModel>(
builder: (ctx,counterVM,child){
return Text("当前计数 : ${counterVM.counter}",style: TextStyle(fontSize: 30),);
}
),
);
}
}
1.3 Consumer的build重新构建问题
不希望重新构建的部分放到builder。然后里面使用child进行包裹
floatingActionButton: Consumer<YHCounterViewModel>(
// 不希望重新构建的部分 可以放到Consumer 里面的 child里面
child: Icon(Icons.add),
builder: (cxt,counterVM,child){
print("floatingActionButton build方法被执行了");
return FloatingActionButton(onPressed: (){
counterVM.counter+=1;
},
child: child,
);
},
),
1.4 不希望 floatingActionButton 重新构建 那么就不使用Consumer
对象 使用Selector
// Selector第二个参数 可以对第一个参数进行一个转换。一般很少用到
// 所以直接 填写两个一样 。那么返回的第二个参数 就直接返回一样的就行
// ShouldRebuild 是不是要重新构建的 返回false 就是不需要重新构建
floatingActionButton: Selector<YHCounterViewModel,YHCounterViewModel>(
selector: (ctx,counverVM) => counverVM,
shouldRebuild: (prev,next) => false,
// 不希望重新构建的部分 可以放到Consumer 里面的 child里面
child: Icon(Icons.add),
builder: (cxt,counterVM,child){
print("floatingActionButton build方法被执行了");
return FloatingActionButton(onPressed: (){
counterVM.counter+=1;
},
child: child,
);
},
),
2.创建多个ViewModel和Provider建立联系
- 使用
MultiProvider
绑定多个ViewModel- Widget关联多个ViewModel 使用 Consumer2、Consumer3、Consumer4、Consumer5、Consumer6
UserInfo.dart
class UserInfo {
String nickname = "";
int level = 0;
String ImageUrl = "";
UserInfo(this.nickname,this.level,this.ImageUrl);
}
YHUserViewModel.dart
import 'package:flutter/material.dart';
import '../model/UserInfo.dart';
class YHUserViewModel extends ChangeNotifier {
UserInfo _user;
YHUserViewModel(this._user);
UserInfo get user => _user;
set user(UserInfo value) {
_user = value;
notifyListeners();
}
}
YHCounterViewModel.dart
// viewmodel
import 'package:flutter/material.dart';
// 一般都继承于 ChangeNotifier
class YHCounterViewModel extends ChangeNotifier {
int _counter = 100;
int get counter => _counter;
set counter(int value) {
_counter = value;
notifyListeners();// 通知所有的监听者 变量发生了改变
}
}
initialize_providers.dart
初始化配置多个ViewModel
import 'package:learn_flutter/day10%E5%85%B1%E4%BA%AB%E7%8A%B6%E6%80%81/viewmodel/counter_view_model.dart';
import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart';
import '../model/UserInfo.dart';
import 'user_view_model.dart';
List<SingleChildWidget> providers = [
ChangeNotifierProvider(create:(ctx)=> YHCounterViewModel()),
ChangeNotifierProvider(create:(ctx)=> YHUserViewModel(UserInfo("宇夜iOS",30,"url"))),
];
main.dart
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:learn_flutter/day10%E5%85%B1%E4%BA%AB%E7%8A%B6%E6%80%81/viewmodel/counter_view_model.dart';
import 'viewmodel/initialize_providers.dart';
import 'package:provider/provider.dart'; // runApp在这个material库里面
import 'viewmodel/user_view_model.dart';
import 'model/UserInfo.dart';
// main() => runApp(MyApp());
/**
* 1.创建自己需要共享的数据
* 2.在应用程序的顶层ChangeNotifierProvider
* 3.在其它位置使用共享的数据
* > Provider.of: 当Provider中的数据发生改变时, Provider.of所在的Widget整个build方法都会重新构建
* > Consumer(相对推荐): 当Provider中的数据发生改变时, 执行重新执行Consumer的builder
* > Selector: 1.selector方法(作用,对原有的数据进行转换) 2.shouldRebuild(作用,要不要重新构建)
*/
void main (){
// runApp(
// //
// ChangeNotifierProvider(
// create: (cxt) =>YHCounterViewModel(),
// child:MyApp() ,
// )
// );
// runApp(
// //
// ChangeNotifierProvider(
// create: (cxt) =>YHCounterViewModel(),
// child:ChangeNotifierProvider(
// create: (cxt) => YHUserViewModel(UserInfo("宇夜iOS",30,"url")),
// child: MyApp(),
// ) ,
// )
// );
runApp(
MultiProvider(providers: providers,
child: MyApp(),
)
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: YHHomePage(),
);
}
}
class YHHomePage extends StatefulWidget {
const YHHomePage({Key? key}) : super(key: key);
@override
State<YHHomePage> createState() => _YHHomePageState();
}
class _YHHomePageState extends State<YHHomePage> {
int _counter = 100;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("InheritedWidget"),
),
// 如果想共享状态 那么需要在最顶层包裹一个继承与 InheritedWidget widget即可
body: YHCounterWidget(
counter:_counter,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
YHShowData01(),
YHShowData02(),
YHShowData03(),
],
),
),
),
// Selector第二个参数 可以对第一个参数进行一个转换。一般很少用到
// 所以直接 填写两个一样 。那么返回的第二个参数 就直接返回一样的就行
// ShouldRebuild 是不是要重新构建的 返回false 就是不需要重新构建
floatingActionButton: Selector<YHCounterViewModel,YHCounterViewModel>(
selector: (ctx,counverVM) => counverVM,
shouldRebuild: (prev,next) => false,
// 不希望重新构建的部分 可以放到Consumer 里面的 child里面
child: Icon(Icons.add),
builder: (cxt,counterVM,child){
print("floatingActionButton build方法被执行了");
return FloatingActionButton(onPressed: (){
counterVM.counter+=1;
},
child: child,
);
},
),
);
}
}
class YHShowData01 extends StatelessWidget {
const YHShowData01({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
int? counter = Provider.of<YHCounterViewModel>(context).counter; // 获取Provider的属性
print("YHShowData01 build");
return Container(
color: Colors.blue,
child:Text("当前计数 : ${counter}",style: TextStyle(fontSize: 30),),
);
}
}
class YHShowData02 extends StatelessWidget {
const YHShowData02({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// int? counter = Provider.of<YHCounterViewModel>(context).counter; // 获取Provider的属性
print("YHShowData02 build");
return Container(
color: Colors.red,
child: Consumer<YHCounterViewModel>(
builder: (ctx,counterVM,child){
print("YHShowData02 Consumer build");
return Text("当前计数 : ${counterVM.counter}",style: TextStyle(fontSize: 30),);
}
),
);
}
}
class YHShowData03 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer2<YHUserViewModel,YHCounterViewModel>(
builder: (ctx,userVM,counterVM,child){
return Text("nickname : ${userVM.user.nickname} , counter :${counterVM.counter}",style: TextStyle(fontSize: 15),);
},
);
}
}
// 共享状态管理
// InheritedWidget的属性是不能够改变的
class YHCounterWidget extends InheritedWidget {
// 1.共享数据
final int counter;
// 2. 定义构造方法
YHCounterWidget({required this.counter, required Widget child}) : super(child: child);
// 3.获取组件最近的InheritedWidget
static YHCounterWidget? of(BuildContext context)
{
// 沿着Element树,去找到最近的YHCounterElement,从Element中取出Widget对象
return context.dependOnInheritedWidgetOfExactType();
}
// 4. 决定要不要回调State中的didChangeDependencies
// 如果返回true:执行依赖当前的InheritedWidget的State中的didChangeDependencies
@override
bool updateShouldNotify(covariant YHCounterWidget oldWidget) {
return oldWidget.counter != counter;
}
}
效果图- Provider的综合案例
1.使用of,Consumer、Consumer2、Selector、
2. MultiProvider创建多个ViewModel