Flutter 全局List Widget 不刷新问题

Posted

tags:

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

参考技术A 我是在StatefulWidget里面设置了一个全局的List,然后动态改变List里面的内容,然后使用setState刷新,结果发现没有用,点击一下虽然会多出一个新的Item,但是还是原来那个,就是List长度、内容不同,Widget显示的是同一个Widget。
比如:九宫格添加图片,第一个是添加图片,点击添加图片把图片放到List里面,然后setState刷新,结果发现页面没变化,触碰添加图片Item,就会多出一个添加图片Item。

根据Flutter基于不可变数据的原理,我们直接把List换一个引用,创建一个新的List。

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 全局List Widget 不刷新问题的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 局部Widget刷新

Flutter 局部Widget刷新

在Flutter中刷新小部件外的列表视图?

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

Flutter局部刷新方法

由于类型问题,Flutter 添加到 List<Widget> 崩溃