Flutter -- 15.Key

2021-11-26  本文已影响0人  MissStitch丶

一.引入key的概念

1.使用StatefulWidget

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Home(),
    );
  }
}

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

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

class _HomeState extends State<Home> {

  final List<Widget> _widgets = [const StateFulTest('1111'), const  StateFulTest('2222'), const StateFulTest('33333')];

  void _onPressed() {
    setState(() {
      _widgets.removeAt(0);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: const Text('Key Demo'),),
        floatingActionButton: FloatingActionButton(
          onPressed: _onPressed,
          child: const Icon(Icons.add),
        ),
        body: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: _widgets,
        )
    );
  }
}

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

  final String title;

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

class _StateFulTestState extends State<StateFulTest> {
  Color color = Color.fromRGBO(Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1.0);

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 100,
      width: 100,
      color: color,
      child: Center(
        child: Text(widget.title),
      ),
    );
  }
}


class StatelessTest extends StatelessWidget {
  StatelessTest(this.title, {Key? key}) : super(key: key);

  final String title;

  Color color = Color.fromRGBO(Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1.0);

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 100,
      width: 100,
      color: color,
      child: Center(
        child: Text(title),
      ),
    );
  }
}
key_colors_bug.gif

2.使用StatelessWidget

3.使用StatelessWidget,将State中的color放到Widget

4.问题排查思路

static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }

5.当我们移除一个Widget时,同时再添加一个Widget,此时的Element是否会复用移除的?

  final List<Widget> _widgets = [const StateFulTest('1111'), const  StateFulTest('2222'), const StateFulTest('33333')];
  
  void _onPressed() {
    setState(() {
      _widgets.removeAt(0);
      _widgets.add(const StateFulTest('4444'));
    });
  }
1.添加一个不带key的Widget

按上图逻辑,Element3会被释放
那么现在断点调试,查看一下Flutter在这块是怎么优化的
此时断点断在createElement()

  StatefulElement createElement() => StatefulElement(this);

结果:并没有进入断点,添加新的Widget(不含key)没有执行createElement

2.添加一个带key的Widget
  void _onPressed() {
    setState(() {
      _widgets.removeAt(0);
      _widgets.add(const StateFulTest('4444', key: ValueKey(4),));
    });
  }

结果:进入断点,添加新的Widget(含key)执行createElement

3.总结

二.Key

abstract class Key {
  /// Construct a [ValueKey<String>] with the given [String].
  ///
  /// This is the simplest way to create keys.
  const factory Key(String value) = ValueKey<String>;

  /// Default constructor, used by subclasses.
  ///
  /// Useful so that subclasses can call us, because the [new Key] factory
  /// constructor shadows the implicit constructor.
  @protected
  const Key.empty();
}
final List<Widget> _widgets = [const StateFulTest('1111', key: Key('1111'),), const  StateFulTest('2222', key: Key('2222')), const StateFulTest('33333', key: Key('33333'))];
key_colors_ferfect.gif

三.LocalKey

1.ValueKey
2.ObjectKey
3.UniqueKey
keyDemo() {

  //创建测试对象
  TestKeyClass testK = TestKeyClass();

  //ValueKey
  ValueKey key1 = ValueKey(testK);
  ValueKey key2 = ValueKey(testK);
  ValueKey key3 = const ValueKey(3);
  print(key1 == key2); //true
  print(key1 == key3); //false

  //ObjectKey
  ObjectKey objectKey1 = ObjectKey(testK);
  ObjectKey objectKey2 = ObjectKey(testK);
  ObjectKey objectKey3 = ObjectKey(TestKeyClass());
  print(objectKey1 == objectKey2); //true
  print(objectKey1 == objectKey3); //false

  //UniqueKey
  print(UniqueKey() == UniqueKey()); //false
}

四.GlobalKey

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

class MyApp extends StatelessWidget {
  MyApp({Key? key}) : super(key: key);

  final GlobalKey _globalKey = GlobalKey();

  void _onPressed() {
    _GlobalKeyTestState state = _globalKey.currentState as _GlobalKeyTestState ;

    /*
    * 下面写法会报出警告
    * The member 'setState' can only be used within instance members of subclasses of 'package:flutter/src/widgets/framework.dart'.
    * 大致意思是setState这个方法应该只能在state方法里面调用
    * 因此这里写了一个refreshState方法中转一下来消除警告
    * */
    // state.setState(() {
    //   state.count ++;
    // });

    state.count ++;
    state.refreshState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Global Key Demo'),),
        body: GlobalKeyTest(key: _globalKey,),
        floatingActionButton: FloatingActionButton(
          onPressed: _onPressed,
          child: const Icon(Icons.add),
        ),
      ),
    );
  }

}

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

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

class _GlobalKeyTestState extends State<GlobalKeyTest> {

  var count = 0;

  refreshState() {
    setState(() {
    });
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text('$count'),
    );
  }
}
globalKey.gif
上一篇下一篇

猜你喜欢

热点阅读