hnvn/flutter_flip_panel

Flip panel doesn't repaint while in background

klisiewicz opened this issue · 0 comments

Hello,

I've created a simple page with FlipPanel that uses streams to display to countdown timer value:

Zrzut ekranu 2019-11-26 o 17 21 10

I've also created another page which is a full screen dialog that covers the main page:

Zrzut ekranu 2019-11-26 o 17 21 26

whenever I go back to the main page the FlipPanel doesn't repaints properly in accordance to the events that took place while it was in the background:

Zrzut ekranu 2019-11-26 o 17 21 34

Only when a foreground event takes place the FlipPanel repaints properly:

Zrzut ekranu 2019-11-26 o 17 21 40

The whole application code:

import 'package:flip_panel/flip_panel.dart';
import 'package:flutter/material.dart';
import 'package:rxdart/subjects.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CountdownProvider(
      child: MaterialApp(
        title: 'Flip Panel Demo',
        theme: ThemeData(primarySwatch: Colors.lightGreen),
        home: MainPage(),
      ),
    );
  }
}

class MainPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final countdown = CountdownProvider.of(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Main Page'),
      ),
      body: Column(
        mainAxisSize: MainAxisSize.max,
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: <Widget>[
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            mainAxisSize: MainAxisSize.max,
            children: <Widget>[
              TimeFlipDigit(
                stream: countdown.tensSecondDigitStream,
                initial: countdown.tensSecondDigit,
              ),
              SizedBox(width: 10),
              TimeFlipDigit(
                stream: countdown.onesSecondDigitStream,
                initial: countdown.onesSecondDigit,
              ),
            ],
          ),
          StreamBuilder(
            stream: countdown.stream,
            builder: (context, snapshot) {
              return snapshot.hasData
                  ? Text(snapshot.data.toString())
                  : SizedBox();
            },
          ),
          RaisedButton(
            color: Theme.of(context).accentColor,
            child: Text('Navigate to other page'),
            onPressed: () {
              Navigator.of(context).push(
                MaterialPageRoute(
                  fullscreenDialog: true,
                  builder: (context) => SubPage(),
                ),
              );
            },
          )
        ],
      ),
    );
  }
}

class SubPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final countdown = CountdownProvider.of(context);
    return Scaffold(
      appBar: AppBar(title: Text('Sub page')),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisSize: MainAxisSize.max,
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: <Widget>[
          StreamBuilder(
            stream: countdown.stream,
            builder: (context, snapshot) {
              return snapshot.hasData
                  ? Text(snapshot.data.toString())
                  : SizedBox();
            },
          ),
          Text('Wait for tens second to change then go back'),
        ],
      ),
    );
  }
}

class CountdownProvider extends InheritedWidget {
  CountdownProvider({
    Key key,
    @required Widget child,
  })  : assert(child != null),
        super(key: key, child: child) {
    _subject.addStream(Countdown(Duration(seconds: 59)).stream);

    _tensSecondDigitSubject
        .addStream(_subject.map<int>((d) => (d.inSeconds % 60) ~/ 10));
    _onesSecondDigitSubject
        .addStream(_subject.map<int>((d) => (d.inSeconds % 60) % 10));
  }

  final _subject = BehaviorSubject<Duration>();
  final _tensSecondDigitSubject = BehaviorSubject<int>();
  final _onesSecondDigitSubject = BehaviorSubject<int>();

  Stream<Duration> get stream => _subject.stream;

  Stream<int> get tensSecondDigitStream => _tensSecondDigitSubject.stream;

  Stream<int> get onesSecondDigitStream => _onesSecondDigitSubject.stream;

  int get tensSecondDigit => _tensSecondDigitSubject.value ?? 0;

  int get onesSecondDigit => _tensSecondDigitSubject.value ?? 0;

  static CountdownProvider of(BuildContext context) =>
      context.inheritFromWidgetOfExactType(CountdownProvider);

  @override
  bool updateShouldNotify(InheritedWidget _) => false;
}

class Countdown {
  Countdown(
    this.duration, {
    this.frequency = const Duration(seconds: 1),
  });

  final Duration duration;
  final Duration frequency;

  Duration _remaining;

  Duration get remaining => _remaining;

  Stream<Duration> get stream async* {
    _remaining = duration;
    while (_remaining >= const Duration()) {
      yield _remaining;
      _remaining -= frequency;
      await Future.delayed(frequency);
    }
  }
}

class TimeFlipDigit extends StatelessWidget {
  final Stream<int> stream;
  final int initial;

  const TimeFlipDigit({
    Key key,
    @required this.stream,
    this.initial = 0,
  })  : assert(stream != null),
        super(key: key);

  @override
  Widget build(BuildContext context) {
    return FlipPanel<int>.stream(
      itemStream: stream,
      initValue: initial,
      direction: FlipDirection.down,
      itemBuilder: (context, value) {
        return Container(
          width: 46,
          height: 72,
          decoration: BoxDecoration(
            color: Colors.lightGreen,
            borderRadius: BorderRadius.horizontal(
              left: Radius.circular(3),
              right: Radius.circular(3),
            ),
          ),
          child: Center(
            child: Text(
              '$value',
              style: Theme.of(context).textTheme.display2,
              textAlign: TextAlign.center,
            ),
          ),
        );
      },
    );
  }
}