Kévin
Kévin

Reputation: 557

Flutter StatefulWidget is not being rebuild

I'm facing some problems with this code.. I moved my widget "ComponentsHistoriqueDeployment" to a statefulwidget with initState() to solve some problem on focus that were rebuilding each time the widget. So actually the data is fetched the first time, but it doesn't change when i tape something in searchbar "Sélectionnez le composant" or by changing the datePicker.

I don't understand why...

This is the parent :

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))
            ],
          ),
        ),
      ));
}

And the widget that should be rebuilt :

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();
                      },
                    ),
                  ]),
            ),
          ],
        ),
      ),
    );
  }
}

Upvotes: 0

Views: 1233

Answers (1)

mkobuolys
mkobuolys

Reputation: 5353

That's expected behaviour: initState() is only called once during widget initialization. Even though properties change their value, State object is not recreated, hence getGroupsList() is not called.

What I would recommend you to do in this case is to move the getGroupsList() up to _HistoriquePageState widget and call it on search value or date range change. Then, instead of passing searchedValue and dateRange properties to ComponentsHistoriqueDeployment, pass the listOfGroups value.

This way, you ensure that getGroupsList() is called every single time as well as the UI is updated.

Upvotes: 1

Related Questions