Flutter 组件刷新【实战】附demo

介绍一下数据与Widget之间传递,数据改变-->触发Widget刷新


1.0 所需工具

  • Provider

2.0 Provider

Provider 会在WidgetModel之间建立监听,以确保数据发生变化时刷新Widget

3.0 必要步骤:

  • Model 要继承ChangeNotifier
  • Widget要监听Model数据变化

Provider 是flutter推荐使用的状态管理工具,具体原理这里不展开分析,下面介绍一下如何快速使用,达到一个真实业务的场景.

4.0 Model创建

// 重要: model必须继承ChangeNotifier
class TestClass with ChangeNotifier {
  var count = 1;

  action() {
    ++count;
    //数据变化后腰通过 notifyListeners() 通知所有监听者
    notifyListeners();
  }
}

4.1 监听Model

Model监听有两种方式

  1. 通过addListener方式
  2. 通过Provider.of<T>(context)

4.1.1 通过 addListener 监听数据改变

当数据改变并且调用 notifyListeners() 这里会收到回调

model.addListener(() { 
      //Do something
});

4.1.2 通过 Provider.of<T>(context) 方式监听

这里看到在build方法中我们获取Provider传入的Model.并没有调用addListener.这里解释一下.
通过查看 Provider.of 源码中知道, Provider.of通过 context 将本次构建的build方法绑定,并监听数据改变. 所以在数据变化后将自动调用 build 方法进行刷新.

  @override
  Widget build(BuildContext context) {
    final model = Provider.of<TestClass>(context);
    return Container()
  }

5.1 向下传递完整示例

下面将一个Model向下传递的完整实例贴出来.

import 'package:provider/provider.dart';

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

class TestClass with ChangeNotifier {
  var count = 1;
  action() {
    ++count;
    notifyListeners();
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<TestClass>(
      create: (context)=>TestClass(),
      child: MaterialApp(
        theme: ThemeData.dark(),
        home: FirstScreen(),
      ),
    );
  }
}

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final model = Provider.of<TestClass>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Provider'),
      ),
      body: Container(
        child: Center(child: Text("${model.count}",style: TextStyle(fontSize: 50),),),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => model.action(),
        child: Icon(Icons.add),
      ),
    );
  }
}

5.2 组件内部通过Model控制数据刷新 完整示例

import 'package:provider/provider.dart';

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

class TestClass with ChangeNotifier {
  var count = 1;
  action() {
    ++count;

    notifyListeners();
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark(),
      home: FirstScreen(),
    );
  }
}

class FirstScreen extends StatelessWidget {
  final TestClass source=TestClass();
  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: Text('Provider'),
      ),
      body:ChangeNotifierProvider<TestClass>(
        create: (context) => source,
        child: Consumer<TestClass>(
          child: Container(),
          builder: (context, model, child) {
            return Container(
              child: Center(child: Text("${model.count}",style: TextStyle(fontSize: 50),),),
            );
          },
        ),
      ),
      
      floatingActionButton: FloatingActionButton(
        onPressed: () => source.action(),
        child: Icon(Icons.add),
      ),
    );
  }
}

6.0 Provider封装

5.2 数据内部控制刷新示例看出,实际代码写起来比较麻烦.所以封装了一下. 只需要传入必要参数就可以.

6.1 封装Widget

class RefreshWidget<T extends ChangeNotifier> extends StatelessWidget {
  RefreshWidget(
      this.builder, {
        @required this.source,
        this.child,
      });

  final T source;
  final Function(BuildContext context, T value, Widget child) builder;
  final Widget child;

  static Builder<T>(
      Function(BuildContext context, T value, Widget child) builder) {
    return builder;
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<T>.value(
      value: source,
      child: Consumer<T>(
        child: child,
        builder: (context, model, child) {
          return builder(context, model, child);
        },
      ),
    );
  }
}

6.2 封装后使用对比

封装前

ChangeNotifierProvider<TestClass>(
        create: (context) => source,
        child: Consumer<TestClass>(
          child: Container(),
          builder: (context, model, child) {
            return Container(
              child: Center(child: Text("${model.count}",style: TextStyle(fontSize: 50),),),
            );
          },
        ),
      ),

封装后

RefreshWidget<TestClass>(
          (context, value, child) => Container(
                child: Center(
                  child: Text(
                    "${value.count}",
                    style: TextStyle(fontSize: 50),
                  ),
                ),
              ),
          source: source),
封装后我们只需要关心下面三点,对与新手比较友好,不用纠结太多
  • builder构建方法(自动补全)
  • source数据model
  • <T> model 类型

直接使用builder方法没有方法提示, 这点很不友好.
封装后将builder放到首位, 会将builder方法直接补全, 这才是我最终的目的, 可以有效减少control+c/v的次数

代码很少直接贴完整Demo出来

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

final Color darkBlue = Color.fromARGB(255, 18, 32, 47);

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

class TestClass with ChangeNotifier {
  var count = 1;
  action() {
    ++count;
    notifyListeners();
  }
}

class MyWidget extends StatelessWidget {
  var _value = TestClass();

  @override
  Widget build(BuildContext context) {
    return Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          GestureDetector(
            child:
            Text('Hello, World!  Click here~~~', style: Theme.of(context).textTheme.headline4),
            onTap: _value.action,
          ),
          RefreshWidget<TestClass>(
              RefreshWidget.b((context, value, child) => Container(
                child: Center(
                  child: Text(
                    "${value.count}",
                    style: TextStyle(fontSize: 50),
                  ),
                ),
              )),
              source: _value)
        ]);
  }
}

class RefreshWidget<T extends ChangeNotifier> extends StatelessWidget {
  RefreshWidget(
      this.builder, {
        @required this.source,
        this.child,
      });

  final T source;
  final Function(BuildContext context, T value, Widget child) builder;
  final Widget child;

  static b<T>(
      Function(BuildContext context, T value, Widget child) builder) {
    return builder;
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<T>.value(
      value: source,
      child: Consumer<T>(
        child: child,
        builder: (context, model, child) {
          return builder(context, model, child);
        },
      ),
    );
  }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。