Harsh Bhavsar
Harsh Bhavsar

Reputation: 1671

Flutter - setState is not Updating inner Custom Stateful widget

I have created a Custom Segments widget which creates Multiple TABS according to List. I am updating selectionsList from homepage.dart but still, my segments are not updating runtime according to changed selectionsList

Segments.dart (Custom SegmentWidget which creates Cupertino tabs)

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class SegmentsWidget extends StatefulWidget {
  @override
  _SegmentsWidgetState createState() => _SegmentsWidgetState();

  final List selectionsList;
  final ValueChanged<int> onSelectTab;
  final VoidCallback onTap;
  final int selectedValue;

  SegmentsWidget(
      {this.selectionsList, this.onSelectTab, this.onTap, this.selectedValue});
}

class _SegmentsWidgetState extends State<SegmentsWidget> {
  Map<int, Widget> tabWidget = Map<int, Widget>();
  int selectedTab = 0;


  @override
  void initState() {
    super.initState();
    print("INit State ${widget.selectionsList}");
    setState(() {
      widget.selectionsList.asMap().forEach((index, value) {
        tabWidget.addAll({
          index: Container(
              height: 40,
              child: Center(
                child: Text(
                  widget.selectionsList[index],
                  style: TextStyle(fontFamily: 'Exo2', fontSize: 12.0),
                ),
              ))
        });
      });
    });
  }

  @override
  void didUpdateWidget(SegmentsWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("Did update");
  }

  @override
  Widget build(BuildContext context) {

    return Container(
      child: Row(
        children: <Widget>[
          Expanded(
            child: SizedBox(
              width: MediaQuery.of(context).size.width,
              child: CupertinoSegmentedControl<int>(
                padding: EdgeInsets.symmetric(vertical: 8),
                children: tabWidget,
                onValueChanged: (int index) {
                  setState(() {
                    selectedTab = index;
                  });
                  widget.onSelectTab(index);
                },
                groupValue: widget.selectedValue ?? selectedTab,
              ),
            ),
          )
        ],
      ),
    );
  }
}

HomePage.dart

From Home Page, I am updating selection array but still my segments are not updating according to selectionList.

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {

  List<String> selection;
  int selectedTab = -1;

  @override
  void initState() {
    super.initState();
    selection = ["A", "B"];
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Dynamic Segments")),
      body: Container(
        child: Column(
          children: <Widget>[

            SegmentsWidget(
              selectionsList: selection,
              onSelectTab: (selectTab) {
                setState(() {
                  selectedTab = selectTab;
                });
              },
            ),

            RaisedButton(child: Text("AB"),onPressed: (){
              setState(() {
                selection = ["A", "B"];
              });

            }),


            RaisedButton(child: Text("ABC"),onPressed: (){
              setState(() {
                selection = ["A", "B", "C"];
              });

            }),

          RaisedButton(child: Text("ABCD"),onPressed: (){
            setState(() {
              selection = ["A", "B", "C","D"];
            });

      })
          ],
        ),
      ),
    );
  }
}

I assume that initState of Segment Widget called once only. I even tried in didUpdateWidget but still not getting updated tabs.

Issue: How to update tabWidgets which is mentioned in my custom widget from another stateful widget?

Upvotes: 1

Views: 1399

Answers (1)

Hamed Rahimvand
Hamed Rahimvand

Reputation: 630

I change some parts of code. Instead of calling your code (that need to be called again on setState) in initState() function, call your code inside the widget with your own method.

see getTabChilds() function below of code.

class _SegmentsWidgetState extends State<SegmentsWidget> {
  Map<int, Widget> tabWidget = Map<int, Widget>();
  int selectedTab = 0;

  @override
  void initState() {
    super.initState();
    print("INit State ${widget.selectionsList}");
  }

  @override
  void didUpdateWidget(SegmentsWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("Did update");
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Row(
        children: <Widget>[
          Expanded(
            child: SizedBox(
              width: MediaQuery.of(context).size.width,
              child: CupertinoSegmentedControl<int>(
                padding: EdgeInsets.symmetric(vertical: 8),
                children: getTabChilds(),
                onValueChanged: (int index) {
                  setState(() {
                    selectedTab = index;
                  });
                  widget.onSelectTab(index);
                },
                groupValue: widget.selectedValue ?? selectedTab,
              ),
            ),
          )
        ],
      ),
    );
  }

  Map<int, Widget> getTabChilds() {
    tabWidget = Map<int, Widget>();
    widget.selectionsList.asMap().forEach((index, value) {
      tabWidget.addAll({
        index: Container(
            height: 40,
            child: Center(
              child: Text(
                widget.selectionsList[index],
                style: TextStyle(fontFamily: 'Exo2', fontSize: 12.0),
              ),
            ))
      });
    });
    return tabWidget;
  }
}

It's tested and works fine.

Upvotes: 2

Related Questions