Почему добавление виджета с отслеживанием состояния в конец списка TabItems вызывает initState и dispose?

#flutter #dart

#flutter #dart

Вопрос:

У меня есть defaultTabController, который отображает вкладки и TabBarView на основе списка TabItem (title, widget), где заголовок соответствует заголовку на вкладке, а widget — виджет с отслеживанием состояния для отображения в TabBarView.

Всякий раз, когда я widgetList.add() добавляю новый TabItem в этот список TabItem, вызываются initState() и dispose() этого виджета с отслеживанием состояния. Но когда я widgetList.insert, это не так.

Просто любопытно, почему это происходит только тогда, когда оно добавляется в конец списка.

Вот панель управления, которая показывает это. Просто прокомментируйте и раскомментируйте соответствующим образом при опробовании widgetList.add и widgetList.insert: https://dartpad.dev/?id=d84d7236a50c0c4769427fcc8b287f41amp;null_safety=true

Спасибо!

Ответ №1:

Я наблюдал, рассматривая разные сценарии.

  1. widgetList пуст и использует add
 List<TabItem> widgetList = [];
..
// added this count to check which widget is calling init or dispose
widgetList.add(TabItem("Added", AWidget(count: count)));
 
  • Начальный: нет виджета
  • Первое нажатие: добавляет виджет (печатает init 0)
  • Второе нажатие: добавляет другой виджет (печатает init1 dispose1)
  • Дальнейшие нажатия: ничего не печатается
  1. Список виджетов, содержащий 3 вкладки и использующий add
 List<TabItem> widgetList = [];
..
// added this count to check which widget is calling init or dispose
List<TabItem> widgetList = [
    TabItem("Test", const AWidget(count: 0)),
    TabItem("Test", const AWidget(count: 1)),
    TabItem("Test", const AWidget(count: 2))
  ];
 
  • Начальный: содержит 2 вкладки (выводит init0)
  • Дальнейшие нажатия: ничего не печатается
  1. widgetList пуст и использует insert
 List<TabItem> widgetList = [];
..
// added this count to check which widget is calling init or dispose
widgetList.insert(count, TabItem("Added", AWidget(count: count)));
 
  • Начальный: нет виджета
  • Первое нажатие: добавляет виджет (печатает init 0)
  • Второе нажатие: добавляет другой виджет (печатает init1 dispose1)
  • Дальнейшие нажатия: ничего не печатается
  1. Список виджетов, содержащий 3 вкладки и использующий insert
 List<TabItem> widgetList = [
    TabItem("Test", const AWidget(count: 0)),
    TabItem("Test", const AWidget(count: 1)),
    TabItem("Test", const AWidget(count: 2))
  ];
..
// added this count to check which widget is calling init or dispose
widgetList.insert(count, TabItem("Added", AWidget(count: count)));
 
  • Начальный: содержит 2 вкладки (выводит init0)
  • Дальнейшие нажатия: ничего не печатается

Учитывая все 4 случая: вы получаете init и dispose только один раз, т.е. Когда присутствует только 1 вкладка, и вы добавляете 2-ю вкладку. Это потому, что добавленная вкладка инициализируется при добавлении в TabBar и немедленно удаляется.

Я предполагаю, что панель вкладок инициализирует (предполагаемую) функциональность при добавлении 2-й вкладки, а затем работает нормально, когда добавляются остальные.

Обычно список из двух или более виджетов вкладок.

Мое предположение было основано на этом утверждении отсюда.

Что я могу с уверенностью сказать, так это то, что такое поведение вызвано, TabBar а не List .

add и insert работает аналогично и никак не влияет на это поведение.

Между тем, когда вы переходите на другие вкладки, скажем, с 0-> 1. Затем он печатает, init1 , dispose0 . Это потому, что при удалении от виджета он инициализирует новый виджет и удаляет ранее использованный (точно так же, как действие навигации).

Я также хочу указать еще на одну вещь. Рассмотрите возможность использования add вместо insert , если это не требуется из-за сложности времени.

Полный код, который я использовал для тестирования (включая количество):

 import 'package:flutter/material.dart';

class TabItem {
  final String title;
  final Widget widget;

  TabItem(this.title, this.widget);
}

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

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

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

class _MyHomePageState extends State<MyHomePage> {
  List<TabItem> widgetList = [];
  int _count = 0; // use 3 when using below commented code
  
//   List<TabItem> widgetList = [
//     TabItem("Test", const AWidget(count: 0)),
//     TabItem("Test", const AWidget(count: 1)),
//     TabItem("Test", const AWidget(count: 2))
//   ];

  void _incrementCounter(int count) {
    setState(() {
      widgetList.add(TabItem("Added", AWidget(count: count)));
//       widgetList.insert(count, TabItem("Added", AWidget(count: count)));
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Main Page'),
      ),
      body: DefaultTabController(
        length: widgetList.length,
        child: Builder(
          builder: (BuildContext context) {
            return Scaffold(
              appBar: AppBar(
                title: const Text("Press floating button to add to list"),
                bottom: TabBar(
                  isScrollable: true,
                  tabs: <Tab>[
                    for (var i in widgetList)
                      Tab(
                        text: i.title,
                      )
                  ],
                ),
              ),
              body: TabBarView(
                  children: [for (var i in widgetList) i.widget]),
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _incrementCounter(_count  );
        },
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

class AWidget extends StatefulWidget {
  const AWidget({
    Key? key,
    this.count=0,
  }) : super(key: key);
  
  final int count;

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

class _AWidgetState extends State<AWidget> {
  @override
  void initState() {
    super.initState();
    print('initState ${widget.count}');
  }

  @override
  void dispose() {
    print('dispose ${widget.count}');
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Text('something ${widget.count}');
  }
}