Flutter StatefulWidget 没有被重建

Posted

技术标签:

【中文标题】Flutter StatefulWidget 没有被重建【英文标题】:Flutter StatefulWidget is not being rebuild 【发布时间】:2021-08-18 06:25:27 【问题描述】:

我在使用这段代码时遇到了一些问题.. 我将我的小部件“ComponentsHistoriqueDeployment”移动到带有 initState() 的 statefulwidget,以解决每次小部件时正在重建的焦点问题。 所以实际上数据是第一次获取的,但是当我在搜索栏“Sélectionnez le composant”中录制某些内容或更改 datePicker 时,它不会改变。

我不明白为什么......

这是父母:

class HistoriquePage extends StatefulWidget 
  final String pageName;
  final String namespace;

  const HistoriquePage(Key? key, required this.pageName, required this.namespace) : super(key: key);

  @override
  _HistoriquePageState createState() => _HistoriquePageState();


class _HistoriquePageState extends State<HistoriquePage> 
  final _debounce = Debounce();
  DateTimeRange? dateRange;
  String searchedValue = "";
  Post? user;

  void _sendSearch(String value) 
    _debounce.run(() 
      setState(() 
        searchedValue = value;
      );
    );
  

  @override
  Widget build(BuildContext context) => GestureDetector(
      onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
      child: Scaffold(
        appBar: AppBar(
          title: Text(widget.pageName),
        ),
        body: SingleChildScrollView(
          child: Column(
            children: [
              Container(
                child: DateRangeField(
                    enabled: true,
                    firstDate: new DateTime(2020),
                    helpText: 'Sélectionnez un interval de dates',
                    fieldStartLabelText: 'Date de début',
                    fieldEndLabelText: 'Date de fin',
                    fieldStartHintText: 'Début',
                    fieldEndHintText: 'Fin',
                    dateFormat: DateFormat('dd/MM/yyyy'),
                    saveText: 'OK',
                    decoration: InputDecoration(
                      prefixIcon: Icon(Icons.date_range, color: Theme.of(context).primaryColor),
                      hintText: 'Sélectionnez un intervalle de dates',
                      hintStyle: Theme.of(context).textTheme.headline6,
                      border: OutlineInputBorder(),
                    ),
                    onChanged: (value) 
                      setState(() 
                        dateRange = value!;
                      );
                    ),
              ),
              Container(
                padding: EdgeInsets.all(16),
                child: TextField(
                    decoration: InputDecoration(
                        prefixIcon: Icon(Icons.search, color: Theme.of(context).primaryColor),
                        border: OutlineInputBorder(),
                        labelText: 'Sélectionnez le composant',
                        labelStyle: Theme.of(context).textTheme.headline6),
                    onChanged: _sendSearch),
              ),
              Container(height: MediaQuery.of(context).size.height - 150, child: ComponentsHistoriqueDeployment(searchedValue, dateRange: dateRange))
            ],
          ),
        ),
      ));

以及应该重建的小部件:

class ComponentsHistoriqueDeployment extends StatefulWidget 
  ComponentsHistoriqueDeployment(this.searchedValue, this.dateRange, Key? key) : super(key: key);

  final String searchedValue;
  final DateTimeRange? dateRange;

  @override
  ComponentsHistoriqueDeploymentState createState() => ComponentsHistoriqueDeploymentState();


class ComponentsHistoriqueDeploymentState extends State<ComponentsHistoriqueDeployment> 
  List<User>? listOfGroups;

  @override
  void initState() 
    getGroupsList();
    super.initState();
  

  getGroupsList() async 
    listOfGroups = await HistoriqueService.fetchHistorique(widget.searchedValue, dateRange: widget.dateRange);
    setState(() 
      listOfGroups = listOfGroups;
    );
  

  @override
  Widget build(BuildContext context) 
    return listOfGroups == null
        ? Center(child: CircularProgressIndicator())
        : ListView.separated(
            separatorBuilder: (BuildContext context, int index) => const Divider(),
            itemCount: listOfGroups!.length,
            itemBuilder: (context, index) 
              return Card(
                child: Column(children: [
                  Padding(
                    padding: const EdgeInsets.only(top: 8),
                    child: Badge(
                      toAnimate: true,
                      animationDuration: Duration(seconds: 2),
                      shape: BadgeShape.square,
                      badgeColor: Theme.of(context).primaryColor,
                      borderRadius: BorderRadius.circular(8),
                      badgeContent: Text(listOfGroups![index].name, style: TextStyle(color: Specific.getWhite, fontSize: 16)),
                    ),
                  ),
                  _displayMoreInformationOnComponent(listOfGroups, index, context)
                ]),
              );
            );
  

  Widget _displayMoreInformationOnComponent(result, index, context) 
    return Container(
      child: ListTile(
        title: Text('Tag: ' + result[index].username),
        subtitle: Text('Date: ' + result[index].address.street),
        leading: Icon(Icons.label),
        trailing: Wrap(
          spacing: 20,
          children: <Widget>[
            IconButton(
              icon: Icon(Icons.help),
              onPressed: () => Dialogs.bottomMaterialDialog(
                  msgStyle: TextStyle(color: Theme.of(context).textTheme.bodyText2?.color),
                  msg: 'Tag: ' +
                      result[index].name +
                      '\nStatus: ' +
                      result[index].name +
                      '\nDernier déploiement: ' +
                      result[index].name +
                      '\nType de route: ' +
                      result[index].name +
                      '\nDernier commit par: ' +
                      result[index].name +
                      '\n',
                  title: result[index].name,
                  color: Specific.getOrange,
                  context: context,
                  actions: [
                    IconsButton(
                      text: "OK",
                      iconData: Icons.check_circle,
                      color: Colors.green,
                      textStyle: TextStyle(color: Specific.getWhite),
                      iconColor: Specific.getWhite,
                      onPressed: () 
                        Navigator.of(context).pop();
                      ,
                    ),
                  ]),
            ),
          ],
        ),
      ),
    );
  

【问题讨论】:

【参考方案1】:

这是预期的行为:initState() 在小部件初始化期间仅被调用一次。即使属性更改了它们的值,State 对象也不会重新创建,因此不会调用 getGroupsList()

在这种情况下,我建议您将getGroupsList() 移动到_HistoriquePageState 小部件并在搜索值或日期范围更改时调用它。然后,不要将 searchedValuedateRange 属性传递给 ComponentsHistoriqueDeployment,而是传递 listOfGroups 值。

这样,您可以确保每次调用 getGroupsList() 并更新 UI。

【讨论】:

谢谢,这真的很有帮助! ,我刚刚添加了一个带有initState 的方法来获取HistoriquePage 上的组。因为如果不是,它只是改变了调用:) 是的,当然,初始加载必须在initState()中完成,很好。

以上是关于Flutter StatefulWidget 没有被重建的主要内容,如果未能解决你的问题,请参考以下文章

Flutter学习-有状态的StatefulWidget

Flutter:将传递的参数更改为“StatefulWidget”

Flutter 中的 statefulWidget 中的屏幕未更新

了解 Flutter 中 StatefulWidget 的使用

为啥在 StatefulWidget 中没有定义 build 方法?

Flutter:如何将变量从 StatelessWidget 传递到 StatefulWidget