简体   繁体   中英

Flutter: Update the UI with value from an async function

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.
    );
  }
}

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++;
    });
  }
}

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.

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.
    );
  }
}

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM