1

Here is a very simple Flutter Material "app", with a counter that is displayed in a Text widget, and can be incremented by pressing a floating action button, and copied to the clipboard using an action button in the appbar.

The "straightforward" way to implement this (also implemented in the sample code created by the flutter create command), is to define the state at the home page level (MyHomePage and _MyHomePageState classes). This lets the FloatingActionButton and the AppBar action button easily modify and access the _counter state variable, because they are under the Home Page in the widget tree.

However, this means that any change in the _counter state variable will trigger a redraw/rebuild of the entire home page, including the AppBar and the FloatingActionButton, which do not change.

I would like to move the StatefulWidget at the level of the Text Widget that displays the _counter state variable, that is the only widget that actually changes based on state.

But in this case I don't know the ways (or the best way) to let the AppBar Action button and the FloatingActionButton continue to access and modify the _counter state variable.

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Hello Counter',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Hello Counter'),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.content_copy),
            tooltip: 'Copy to Clipboard',
            // Get counter by accessing state
            onPressed: () => Clipboard.setData(ClipboardData(text: '$_counter')),
          )
        ],
      ),
      body: Center(
        child: Text(
          '$_counter',
          style: Theme.of(context).textTheme.display1,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        // Set counter by changing state
        onPressed: () => setState(() => _counter++),
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
TechAurelian
  • 5,561
  • 5
  • 50
  • 65
  • That's the desired behavior. What is wrong with this? – Rémi Rousselet Sep 17 '18 at 15:38
  • @RémiRousselet Performance issues to rebuild the entire tree / unnecessary widgets like the AppBar and FloatingActionButton that do not change? From the [docs](https://docs.flutter.io/flutter/widgets/StatefulWidget-class.html): "Push the state to the leaves. For example, if your page has a ticking clock, rather than putting the state at the top of the page and rebuilding the entire page each time the clock ticks, create a dedicated clock widget that only updates itself." – TechAurelian Sep 17 '18 at 15:42
  • 1
    But here your whole app depends on counter. There's no way you can have your counter stored further down the tree; and having its parents access it. – Rémi Rousselet Sep 17 '18 at 15:45
  • There's nothing preventing such optimizations while having counter at the root of your screen – Rémi Rousselet Sep 17 '18 at 15:47
  • 1
    Take a look at https://stackoverflow.com/questions/46057353/controlling-state-from-outside-of-a-statefulwidget/51460832#51460832 https://stackoverflow.com/questions/49491860/flutter-how-to-correctly-use-an-inherited-widget/49492495#49492495 https://stackoverflow.com/questions/50430273/how-to-set-state-from-another-widget/50430389#50430389 – Rémi Rousselet Sep 17 '18 at 16:10
  • @RémiRousselet Thank you for the solutions in the links. So in your opinion there are no significant performance issues when using the code as in the example (with state at the parent level)? Does Flutter know how to cache, or not to rebuild/redraw the AppBar or the FloatingActionButton, because they do not change? – TechAurelian Sep 17 '18 at 16:22
  • Your code is not optimized, that's for sure. But you should be able to optimize it by moving the _counter to a parent. Not as a child – Rémi Rousselet Sep 17 '18 at 16:26

0 Answers0