1

I want to see a the value of a counter in a flutter UI when the counter is updated asynchronously.

Staring from the sample flutter project, I would expect the below would make it, but only the final value is displayed. How can I achieve to see the numbers changing from 1 to 100000?

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

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

  void _incrementCounter() async {
    for(int i=0; i<100000; ++i) {
      setState(() {
        _counter++;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
2
  • I think this code updates the UI as expected, do you want to have a history of past numbers ? or what do you mean by asynchronously. Commented Jun 19, 2022 at 15:18
  • A better approach would be yielding those numbers as a Stream and listen to it via StreamBuilder Commented Jun 19, 2022 at 17:41

4 Answers 4

1

I think the issue is just that your loop is running too fast to show the intermediate values. Slowing the loop down with Future.delayed() should let you see what you want.

void _incrementCounter() async {
  for(int i=0; i<100000; ++i) {
    await Future.delayed(Duration(seconds: 1));
    setState(() {
      _counter++;
    });
  }
}
Sign up to request clarification or add additional context in comments.

Comments

0

to see the numbers changing from 1 to 100000 You can use Timer.periodic.

Creating state level timer variable to have control on running state.

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

  void _incrementCounter() async {
    const delay = Duration(milliseconds: 100); // controll update speed
    const numberLimit = 100000;
    _timer = Timer.periodic(delay, (timer) {
      if (_counter < numberLimit) {
        setState(() {
          _counter++;
        });
      } else {
        timer.cancel();
      }
    });
  }

  void _reset() {
    setState(() {
      _counter = 0;
    });
    _timer?.cancel();
  }

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

You can find more about dart-async-library and Timer.periodic on flutter.dev.

Comments

0
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  late Timer _timer;
  int _start = 0;

  void startTimer() {
    const oneSec = const Duration(seconds: 1);
    _timer = new Timer.periodic(
        oneSec,
        (Timer timer) => setState(() {
              if (_start > 100000) {
                timer.cancel();
              } else {
                _start = _start + 1;
              }
            }));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_start',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: startTimer,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

Comments

0

Hey you can use ValueListenableBuilder to notify you state instead of calling setState as it will rebuild whole ui. Read here in more details about ValueListenableBuilder

Below is sample code -

class _MyHomePageState extends State<MyHomePage> {
  Timer? _timer;
  ValueNotifier _valueNotifier = ValueNotifier(0);

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder(
      valueListenable: _valueNotifier,
      builder: (context, value, child) {
        return Text(value.toString());
      },
    );
  }

  void _incrementCounter() async {
    const delay = Duration(milliseconds: 100); // controll update speed
    const numberLimit = 100000;
    _timer = Timer.periodic(delay, (timer) {
      if (_valueNotifier.value < numberLimit) {
        _valueNotifier.value++;
      } else {
        timer.cancel();
      }
    });
  }

  void _reset() {
    _valueNotifier.value = 0;
    _timer?.cancel();
  }

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

1 Comment

I have tried that too but with the same result. At the end, the solution is to slow down the increment. That is ok for the sample indeed, but not for a real application. I would go on the route of ignoring too frequent updates... but honestly I am giving up with Flutter

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.