最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

flutter - Stopwatch StreamBuilder Shows Snapshot in waiting State with Null Data After Reload - Stack Overflow

programmeradmin2浏览0评论

I'm building a stopwatch app in Flutter that uses a StreamBuilder to update elapsed and lap times. The app works fine when the stopwatch is running, but after pausing the stopwatch, leaving the page, and reloading it, the StreamBuilder for elapsed and lap times outputs this:

AsyncSnapshot<Duration>(ConnectionState.waiting, null, null, null)

Because of this, the stopwatch UI resets to 00:00.00 until I start the stopwatch again. I expect it to load and display the correct paused elapsed and lap times immediately after the page is reloaded.

Here's a simplified version of the code for the stopwatch service and UI:

class StopwatchService {
  final StreamController<Duration> _elapsedTimeController = StreamController<Duration>.broadcast();
  final StreamController<Duration> _lapTimeController = StreamController<Duration>.broadcast();

  Duration _pausedDuration = Duration.zero;
  int? _startTimeEpoch;
  int? _lastStopTimeEpoch;

  Stream<Duration> get elapsedTimeStream => _elapsedTimeController.stream;
  Stream<Duration> get lapTimeStream => _lapTimeController.stream;

  void start() {
    _startTimeEpoch ??= DateTime.now().millisecondsSinceEpoch;
    _tick();
  }

  void stop() {
    _lastStopTimeEpoch = DateTime.now().millisecondsSinceEpoch;
    _pausedDuration += Duration(milliseconds: _lastStopTimeEpoch! - _startTimeEpoch!);
    _elapsedTimeController.add(_pausedDuration);
    _lapTimeController.add(Duration.zero); // For simplicity
  }

  void reset() {
    _elapsedTimeController.add(Duration.zero);
    _lapTimeController.add(Duration.zero);
  }

  Future<void> loadState(SharedPreferences prefs) async {
    final lastStopTimeEpoch = prefs.getInt('lastStopTimeEpoch') ?? DateTime.now().millisecondsSinceEpoch;
    final startTimeEpoch = prefs.getInt('startTimeEpoch') ?? 0;
    final pausedDuration = Duration(milliseconds: prefs.getInt('pausedDuration') ?? 0);

    if (startTimeEpoch != 0) {
      final elapsedTime = Duration(milliseconds: lastStopTimeEpoch - startTimeEpoch - pausedDuration.inMilliseconds);
      _elapsedTimeController.add(elapsedTime);
      _lapTimeController.add(Duration.zero);
    }
  }

  void _tick() {
    Timer.periodic(Duration(milliseconds: 10), (timer) {
      if (_startTimeEpoch == null) return;
      final now = DateTime.now().millisecondsSinceEpoch;
      final elapsedTime = Duration(milliseconds: now - _startTimeEpoch! - _pausedDuration.inMilliseconds);
      _elapsedTimeController.add(elapsedTime);
    });
  }
}

class StopwatchPage extends StatefulWidget {
  const StopwatchPage({super.key});

  @override
  StopwatchPageState createState() => StopwatchPageState();
}

class StopwatchPageState extends State<StopwatchPage> {
  final StopwatchService _stopwatchService = StopwatchService();

  @override
  void initState() {
    super.initState();
    _loadState();
  }

  Future<void> _loadState() async {
    final prefs = await SharedPreferences.getInstance();
    await _stopwatchService.loadState(prefs);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: StreamBuilder<Duration>(
        stream: _stopwatchService.elapsedTimeStream,
        builder: (context, snapshot) {
          final elapsed = snapshot.data ?? Duration.zero;
          return Text(elapsed.toString());
        },
      ),
    );
  }
}

I have tried:

Adding Initial Values to Streams: I explicitly emit Duration.zero in _elapsedTimeController and _lapTimeController when loading the state or resetting the stopwatch.

Debugging the StreamBuilder: The StreamBuilder still shows waiting and null data immediately after the page is reloaded.

Forcing UI Updates: Tried manually triggering state updates with setState after emitting values to streams.

Expected Behavior: When the stopwatch page reloads, the StreamBuilder should display the correct paused elapsed and lap times without requiring user input.

Actual Behavior: The StreamBuilder shows AsyncSnapshot<Duration>(ConnectionState.waiting, null, null, null) and resets the display to 00:00.00.

How can I ensure that the StreamBuilder displays the correct paused elapsed and lap times immediately after the page reloads? Is there a best practice for initializing StreamController with previously saved data to avoid this issue?

发布评论

评论列表(0)

  1. 暂无评论