0

I am making this app, but am unsure of a good way to organize it. With the picture to demonstrate below:

enter image description here

Where (currently):

  • Blue: Stateless Widget => ListView, Rows Widgets
  • Red: Stateful Widget => Expanded Widgets
  • Green: Stateless Widget => Expanded Widgets

What the app needs to do: The digits need to update themselves whenever they change (I am not worried about fancy animations right now, just transitioning).

Problem: I found that the only way to update these digits is through Timer.periodic every minute. However, I am finding it hard to route this information. I currently have my periodic timer under each Red Expanded Widget, but there is a unique DateTime used in all of them. Redundant information is the problem with this approach. I could place a periodic timer in the Blue ListView, Rows Widget. However, every update would require a reload of all containing widgets, including the Green Expanded Widgets.

If there are any suggestions or different ways, anything would be appreciated.

1 Answer 1

1

Flutter's design means that it doesn't matter that you reload the green Widgets; the way it works makes that really inexpensive. So, drag your state up to a StatefulWidget containing all your text/columns/etc (which can now be stateless).

This example shows one way of pushing down a fragment of state (into the constructor of DurationPartWidget). I've shown that you could do the date mathematics in the build method. Equally, you could do it in setState and make years, months, etc instance variables of _AnniversaryWidgetState.

class AnniversaryWidget extends StatefulWidget {
  final DateTime firstDate;

  AnniversaryWidget(this.firstDate);

  @override
  State createState() {
    return new _AnniversaryWidgetState();
  }
}

class _AnniversaryWidgetState extends State<AnniversaryWidget> {
  Timer _timer;

  @override
  void initState() {
    super.initState();
    _timer = new Timer.periodic(new Duration(seconds: 1), (Timer t) {
      setState(() {});
    });
  }

  @override
  void dispose() {
    _timer.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    DateTime now = new DateTime.now();
    // todo - calculate these from now minus firstDate
    int years = 0;
    int months = 4;
    int days = 13;
    int hours = 21;
    int minutes = now.difference(widget.firstDate).inMinutes % 60;
    return new Center(
      child: new Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          new _DurationPartWidget('years', years),
          new _DurationPartWidget('months', months),
          new _DurationPartWidget('days', days),
          new _DurationPartWidget('hours', hours),
          new _DurationPartWidget('minutes', minutes),
        ],
      ),
    );
  }
}

class _DurationPartWidget extends StatelessWidget {
  final int _numberPart;
  final String _label;

  _DurationPartWidget(this._label, this._numberPart);

  @override
  Widget build(BuildContext context) {
    return new Row(
      children: <Widget>[
        new Text(_numberPart.toString().padLeft(2, '0')),
        new Text(_label),
      ],
    );
  }
}

If, later, you want to bring you state even higher up your Widget tree, you could use an InheritedWidget. (I sometimes use one above MaterialApp.) Brian Egan gave an awesome talk at DartConf 2018 on this whole topic. https://www.youtube.com/watch?v=zKXz3pUkw9A&list=PLOU2XLYxmsIIJr3vjxggY7yGcGO7i9BK5&index=10

Sign up to request clarification or add additional context in comments.

1 Comment

I'll take a look at the video today. Thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.