Flutter 局部Widget刷新

Posted 123_000000

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter 局部Widget刷新相关的知识,希望对你有一定的参考价值。

更新局部Widget,避免全局刷新 ,总结自己遇到和需要的情况整理如下,原理不做解释,只记录用法

1、GlobalKey 

通过传递GlobalKey 获取Widget的State, 调用 setState(VoidCallback fn) 刷新Widget

注意:使用GlobalKey开销较大,如果有其他可选方案,应尽量避免使用它。另外同一个GlobalKey在整个widget树中必须是唯一的,不能重复 。                                                

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '动态更新叶子节点的Widget',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: '动态更新叶子节点的Widget'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  //设置key,绑定待刷新控件.
  GlobalKey textKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    print('Enter _MyHomePageState.build()');

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            TextWidget(textKey),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _counter++;

          //动态刷新局部widget(widget内部更新)
          (textKey.currentState as TextWidgetState).onPressed(_counter);

        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}


//********使用GlobalKey局部刷新*****/
class TextWidget extends StatefulWidget {
  //! 接收传过来的key
  TextWidget(Key key) : super(key: key);

  @override
  State createState() {
    return TextWidgetState();
  }
}

class TextWidgetState extends State<TextWidget> {
  int _counter = 0;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(
        '$_counter',
        style: TextStyle(fontSize: 20, color: Colors.green),
      ),
    );
  }

  //在TextWidget的onPressed中单独调用TextWidget的setState,刷新本控件.
  void onPressed(int count) {
    setState(() {
      _counter = count;
    });
  }
}

2、替换Wiget,Element update(covariant Widget newWidget) 

感觉类似于android 的切换缓存的Fragment 列表来更新布局。

以下demo,创建两个TextUpdateWidget存于upWidgetPages中,counter奇偶判断切换两个TextUpdateWidget的显示。

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '动态替换叶子节点的Widget',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: '动态替换叶子节点的Widget'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  //用于更换的组件,类似于Android fragment替换更新
  TextUpdateWidget updateWidget;
  //存储组件列表
  List<Widget> upWidgetPages = [];


  static Element findChild(Element e, Widget w) {
    Element child;
    void visit(Element element) {
      if (w == element.widget)
        child = element;
      else
        element.visitChildren(visit);
    }

    visit(e);
    return child;
  }

  @override
  Widget build(BuildContext context) {
    print('Enter _MyHomePageState.build()');

    updateWidget = TextUpdateWidget(counter: _counter);
    upWidgetPages.add(updateWidget);

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            updateWidget,
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          print('Enter floatingActionButton.onPressed()');
          _counter++;
          //创建widget 存于upWidgetPages 用于替换
          if (_counter == 1) {
            TextUpdateWidget updateWidget = TextUpdateWidget(counter: _counter);
            upWidgetPages.add(updateWidget);
          }
          Element e = findChild(context as Element, updateWidget);
          if (e != null) {
            if ((_counter + 1) % 2 == 1) {
              updateWidget = upWidgetPages[0];
            } else {
              updateWidget = upWidgetPages[1];
            }
            e.owner.lockState(() {
              e.update(updateWidget);
            });
          }
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

//*******局部替换Widget********/
class TextUpdateWidget extends StatefulWidget {
  int counter = 0;

  TextUpdateWidget({Key key, this.counter}) : super(key: key);

  @override
  State<StatefulWidget> createState() => TextUpdateWidgetState();
}

class TextUpdateWidgetState extends State<TextUpdateWidget> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: (widget.counter + 1) % 2 == 0
          ? Text('another times: ${widget.counter}',
          style: TextStyle(fontSize: 24, color: Colors.cyan))
          : Text('another times: ${widget.counter}',
          style: TextStyle(fontSize: 28, color: Colors.amberAccent)),
    );
  }
}

3、Provider 状态共享,更新wiget

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // return MultiProvider(
    //   providers: [
    //     //这里是关键注册通知吧
    //     ChangeNotifierProvider(create: (_) => Counter()),
    //   ],
    //   child: MaterialApp(
    //     title: 'Test',
    //     //设置主题
    //     theme: ThemeData(primaryColor: Colors.pink),
    //     home: CountPage(),
    //   ),
    // );

    return ChangeNotifierProvider(
      create: (_) => Counter(),
      child: MaterialApp(
        title: 'Test',
        theme: ThemeData(primaryColor: Colors.pink),
        home: CountPage(),
      ),
    );
  }
}

class CountPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return CountPageState();
  }
}

class CountPageState extends State<CountPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text("你都点击${Provider.of<Counter>(context).value}次了",
                  style: TextStyle(
                    color: Colors.red,
                    fontSize: 33.0,
                  )),
              TextUpdateWidget()
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            Provider.of<Counter>(context, listen: false)..increment();
          },
          tooltip: 'add',
          child: Icon(Icons.add),
        ));
  }
}

class TextUpdateWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => TextUpdateWidgetState();
}

class TextUpdateWidgetState extends State<TextUpdateWidget> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: (Provider.of<Counter>(context).value + 1) % 2 == 0
          ? Text('another times: ${Provider.of<Counter>(context).value}',
              style: TextStyle(fontSize: 24, color: Colors.cyan))
          : Text('another times: ${Provider.of<Counter>(context).value}',
              style: TextStyle(fontSize: 28, color: Colors.amberAccent)),
    );
  }
}

//混入
class Counter with ChangeNotifier {
  int value = 0;

  increment() {
    value += 1;
    //发送通知
    notifyListeners();
  }
}

以上是关于Flutter 局部Widget刷新的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 局部Widget刷新

Flutter 局部Widget刷新

Flutter组件(Widget)的局部刷新方式

Flutter 实现局部刷新

Flutter StreamBuilder 实现局部刷新 Widget

Flutter 全局List Widget 不刷新问题