flutter实战技术

flutter状态管理

2023-11-11  本文已影响0人  江水东流

尽可能减少build方法调用的范围 是状态管理的目的,如果大范围控件调用build 性能会很低.

用ValueListenableBuilder 局部刷新 整体的build方法不会执行


class MSValueListenableBuilderDemo extends StatefulWidget {
  const MSValueListenableBuilderDemo({Key? key}) : super(key: key);

  @override
  State<MSValueListenableBuilderDemo> createState() =>
      _MSValueListenableBuilderDemoState();
}

class _MSValueListenableBuilderDemoState
    extends State<MSValueListenableBuilderDemo> {
  // 定义一个ValueNotifier,当数字变化时会通知 ValueListenableBuilder
  final ValueNotifier<int> _counter = ValueNotifier<int>(0);
  @override
  Widget build(BuildContext context) {
    // 点击 + 按钮不会触发整个 ValueListenableRoute 组件的 build
    print('build');
    return Scaffold(
      appBar: AppBar(title: Text("ValueListenableBuilderDemo")),
      body: Center(
        child: ValueListenableBuilder<int>(
          valueListenable: _counter,
          builder: (ctx, value, child) {
            print('build-----ValueListenableBuilder');
            return Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                child!,
                Text("$value 次", textScaleFactor: 1.5),
              ],
            );
          },
          child: Text("点击了", textScaleFactor: 1.5),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () {
          // 点击后值 +1,触发 ValueListenableBuilder 重新构建
          _counter.value++;
        },
      ),
    );
  }

  void test() {
    ValueNotifier<double> notifier = ValueNotifier<double>(2.0);
    //添加监听者
    final VoidCallback listener = () {
      print('---> ${notifier.value}');
    };
    notifier.addListener(listener);
    //改编值后 监听者可以收到数据
    notifier.value = 3.0;
  }
}

Provider 状态管理
yaml文件添加 provider: ^6.1.1
Provider.of<MyCounter>(context).userInfo.name; 这种使用widget build方法还会调用
Consumer 方法使用时候 只有Consumer 里面的builder方法才会调用


void main() => runApp(MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (ctx) => MyCounter()),
        ChangeNotifierProvider(create: (ctx) => MySubtract()),
      ],
      child: ProviderDemo(),
    ));

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

class UserInfo {
  String name = "leo";
  int age = 10;
  UserInfo(this.name, this.age);
}

/// with ChangeNotifier
class MyCounter with ChangeNotifier {
  UserInfo _userInfo = UserInfo("leo", 10);
  UserInfo get userInfo => _userInfo;

  void add() {
    _userInfo.age++;
    notifyListeners();
  }
}

///  extends ChangeNotifier
class MySubtract extends ChangeNotifier {
  UserInfo _userInfo = UserInfo("jim", 100);
  UserInfo get userInfo => _userInfo;

  void sub() {
    _userInfo.age--;
    notifyListeners();
  }
}

class ProviderDemo extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    print('ProviderDemo build-----');

    return MaterialApp(
      title: 'Flutter widget',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('MyHomePage build-----');

    return Scaffold(
      appBar: AppBar(
        title: Text("Flutter Provider"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            MyContainer1(),
            SizedBox(height: 30),
            MyContainer2()
          ],
        ),
      ),
    );
  }
}

class MyContainer1 extends StatefulWidget {
  @override
  _MyContainer1State createState() => _MyContainer1State();
}

class _MyContainer1State extends State<MyContainer1> {
  @override
  Widget build(BuildContext context) {
    print('MyContainer1 build-----');

    final String name1 = Provider.of<MyCounter>(context).userInfo.name;
    final int age1 = Provider.of<MyCounter>(context).userInfo.age;

    final String name2 = Provider.of<MySubtract>(context).userInfo.name;
    final int age2 = Provider.of<MySubtract>(context).userInfo.age;

    return Column(
      children: <Widget>[
        Text("Container1 name1=$name1  age1=$age1", style: myTextStyle1()),
        Text("Container1 name2=$name2  age2=$age2", style: myTextStyle2()),
      ],
    );
  }
}

TextStyle myTextStyle1() {
  return TextStyle(fontSize: 20, color: Colors.blue);
}

TextStyle myTextStyle2() {
  return TextStyle(fontSize: 20, color: Colors.green);
}

class MyContainer2 extends StatefulWidget {
  @override
  _MyContainer2State createState() => _MyContainer2State();
}

class _MyContainer2State extends State<MyContainer2> {
  @override
  Widget build(BuildContext context) {
    print('_MyContainer2State build-----');
    return Container(
      child: Consumer2<MyCounter, MySubtract>(
        builder: (ctx, counterVM, subtractVM, child) {
          print('_MyContainer2State Consumer2  build-----');
          return Column(
            children: <Widget>[
              Text(
                  "Container2 name1=${counterVM.userInfo.name}  age1=${counterVM.userInfo.age}",
                  style: myTextStyle1()),
              Text(
                  "Container2 name2=${subtractVM.userInfo.name}  age2=${subtractVM.userInfo.age}",
                  style: myTextStyle2()),
              SizedBox(
                height: 30,
              ),
              RaisedButton(
                child: Text("点 击"),
                onPressed: () {
                  counterVM.add();
                  subtractVM.sub();
                },
              ),
            ],
          );
        },
      ),
    );
  }
}

StreamController


  test() {
    StreamSubscription<String> subscription;
    //创建StreamController
    var streamController = StreamController<String>();
    // 获取StreamSink用于发射事件
    StreamSink<String> streamSink = streamController.sink;
    // 获取Stream用于监听
    Stream<String> streamData = streamController.stream;
    //监听事件
    subscription = streamData.listen((value) {
      // do something
    });
    //发射一个事件.
    streamSink.add("111");
    streamController.close();
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    // 4、关流,避免内存泄漏
    streamController.close();
  }
}

import 'dart:async';

import 'package:flutter/material.dart';

class StreamBuildDemo extends StatefulWidget {
  // const StreamBuildDemo({super.key});

  @override
  State<StreamBuildDemo> createState() => _StreamBuildDemoState();
}

class _StreamBuildDemoState extends State<StreamBuildDemo> {
  int a = 0;
  // 1、声明一个StreamController类型的控制器,命名为streamController;
  final StreamController<int> streamController = StreamController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // 2、将需要局部刷新数据的组件嵌套在StreamBuilder组件内,并接收信息;
            StreamBuilder<int>(
              stream: streamController.stream,
              initialData: a,
              builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
                return Text('a : $a');
              },
            ),
            ElevatedButton(
              onPressed: () {
                a++;
                setState(() {});
              },
              child: Text('setState'),
            ),
            ElevatedButton(
              onPressed: () {
                a++;
                // 3、往`StreamBuilder`里添加数据,并通知`StreamBuilder`重新构建;
                streamController.add(a);
              },
              child: Text('streamBuilder'),
            ),
          ],
        ),
      ),
    );
  }

如果只是传递消息可以用 event_bus

import 'dart:async';

class EventBus {
  StreamController _streamController;

  StreamController get streamController => _streamController;

  EventBus({bool sync = false})
      : _streamController = StreamController.broadcast(sync: sync);
  EventBus.customController(StreamController controller)
      : _streamController = controller;

  
  Stream<T> on<T>() {
    if (T == dynamic) {
      return streamController.stream as Stream<T>;
    } else {
      return streamController.stream.where((event) => event is T).cast<T>();
    }
  }

  void fire(event) {
    streamController.add(event);
  }


  void destroy() {
    _streamController.close();
  }
}

//使用示例
final EventBus playerEventBus = EventBus();
    playerEventBus.fire(HibStopVideoPlayerEvent());
  StreamSubscription stopVideoSub;
   @override
  void initState() {
    super.initState();

    // 监控暂停视频事件
    if (null == stopVideoSub) {
      stopVideoSub =
          playerEventBus.on<HibStopVideoPlayerEvent>().listen((event) {
     
        }
      });
    }
  void dispose() {
    super.dispose();

    if (null != stopVideoSub) {
      stopVideoSub.cancel();
      stopVideoSub = null;
    }
}

setState() 方法内部的工作原理如下:

首先,Flutter 框架会记录需要重建的 Widget。

然后,Flutter 框架会调用 build() 方法来重建 Widget。

在 build() 方法中,Flutter 框架会根据 Widget 的新状态来构建 Widget 树,并返回一个新的 Widget 树。

最后,Flutter 框架会比较新旧 Widget 树的差异,并将差异应用到渲染树中,以更新 Widget 的显示。

需要注意的是,setState() 方法并不是立即执行的,而是将其标记为“脏”状态,等到下一次构建时再执行。因此,如果在 setState() 方法调用后立即访问 Widget 的状态,可能得到的还是旧的状态。为了避免这种情况,可以使用 Future.microtask(() {}); 或 WidgetsBinding.instance.addPostFrameCallback() 方法来在下一次构建之后获取 Widget 的最新状态。
setState()的刷新区域如果控制不好的话,会引起大范围的重绘,慎用。

1.Flutter状态管理和数据传递

数据同步关系有以下三种类型

由上往下,传递给子孙节点
由下往上,传递给祖宗节点
兄弟节点传递

同步可能需要满足以下场景: 组件A共享数据给组件B时,

组件B可以实时拿到组件A的变化值,
可以监听到数据变更,
组件B可以通知组件A进行数据更改,
组件A可以决定是否需要重建。

Flutter提供了数据传递的几种方案:

InheritedWidget: 适用于父组件传递给子组件的场景, 可跨层级

Notification:适用于子组件通知父组件数据改变的场景

Broadcast: 消息广播机制

class TestPage extends StatefulWidget {
 const TestPage({Key? key}) : super(key: key);

 @override
 State<TestPage> createState() => _TestPageState();
}

class _TestPageState extends State<TestPage> {
 int count = 0;

 @override
 Widget build(BuildContext context) {
   return Scaffold(
       body: Center(
     child: MyInheritedWidget(
       count: count,
       child: Column(
         mainAxisSize: MainAxisSize.min,
         children: [
           const TestWidget(),
           IconButton(
             onPressed: () {
               setState(() {
                 count++;
               });
             },
             icon: const Icon(Icons.add),
           )
         ],
       ),
     ),
   ));
 }
}

class TestWidget extends StatefulWidget {
 const TestWidget({Key? key}) : super(key: key);

 @override
 State<TestWidget> createState() => _TestWidgetState();
}

class _TestWidgetState extends State<TestWidget> {
 @override
 Widget build(BuildContext context) {
   return Text(
     MyInheritedWidget.of(context)?.count.toString() ?? "",
     style: const TextStyle(
       color: Colors.black,
       fontSize: 14,
       fontWeight: FontWeight.w400,
     ),
   );
 }
}

class MyInheritedWidget extends InheritedWidget {
 final int count;

 const MyInheritedWidget(
     {super.key, required this.count, required Widget child})
     : super(child: child);

 static MyInheritedWidget? of(BuildContext context) {
   return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
 }

 @override
 bool updateShouldNotify(covariant MyInheritedWidget oldWidget) {
   return oldWidget.count != count;
 }
}

Notification适用于子组件通知父组件数据改变的场景,是通过dispatch方法将消息由子到父派发出来的,这种机制叫做通知冒泡,会通知到所有通过NotificationListener来监听的父节点,也可以通过中间的某个节点来中止。

//自定义通知
class CustomNotification extends Notification {
  CustomNotification(this.msg);
  final String msg;
}

在子组件中通过dispatch派发消息

class CustomChild extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      child: Text("Fire Notification"),
      onPressed: () => CustomNotification("lala").dispatch(context),
    );
  }
}

在父组件中通过NotificationListener设置对自定义通知的监听

class CustomNotificationRoute extends StatefulWidget {
  @override
  _CustomNotificationRouteState createState() => new _CustomNotificationRouteState();
}

class _CustomNotificationRouteState extends State<CustomNotificationRoute> {
  String _msg = "通知: ";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NotificationListener<CustomNotification>(
        onNotification: (notification) {
          setState(() {
            _msg += notification.msg + "   ";
          });
      //如何停止通知冒泡?在onNotification函数中返回true即可。默认情况下onNotification返回false表示不阻止冒泡。
          return true;
        },
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[Text(_msg), CustomChild()],
        )
      )
    );
  }
}


上一篇 下一篇

猜你喜欢

热点阅读