// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('Sliver with keep alive without key - should dispose after reordering', (WidgetTester tester) async {
    List<Widget> childList= <Widget>[
      const WidgetTest0(text: 'child 0', keepAlive: true),
      const WidgetTest1(text: 'child 1', keepAlive: true),
      const WidgetTest2(text: 'child 2', keepAlive: true),
    ];
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    final _WidgetTest0State state0 = tester.state(find.byType(WidgetTest0));
    expect(find.text('child 0'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 2', skipOffstage: false), findsNothing);

    childList = createSwitchedChildList(childList, 0, 2);
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    final _WidgetTest2State state2 = tester.state(find.byType(WidgetTest2));
    expect(find.text('child 2'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 0', skipOffstage: false), findsNothing);

    expect(state0.hasBeenDisposed, true);
    expect(state2.hasBeenDisposed, false);
  });

  testWidgets('Sliver without keep alive without key - should dispose after reordering', (WidgetTester tester) async {
    List<Widget> childList= <Widget>[
      const WidgetTest0(text: 'child 0'),
      const WidgetTest1(text: 'child 1'),
      const WidgetTest2(text: 'child 2'),
    ];
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    final _WidgetTest0State state0 = tester.state(find.byType(WidgetTest0));
    expect(find.text('child 0'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 2', skipOffstage: false), findsNothing);

    childList = createSwitchedChildList(childList, 0, 2);
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    final _WidgetTest2State state2 = tester.state(find.byType(WidgetTest2));
    expect(find.text('child 2'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 0', skipOffstage: false), findsNothing);

    expect(state0.hasBeenDisposed, true);
    expect(state2.hasBeenDisposed, false);
  });

  testWidgets('Sliver without keep alive with key - should dispose after reordering', (WidgetTester tester) async {
    List<Widget> childList= <Widget>[
      WidgetTest0(text: 'child 0', key: GlobalKey()),
      WidgetTest1(text: 'child 1', key: GlobalKey()),
      WidgetTest2(text: 'child 2', key: GlobalKey()),
    ];
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    final _WidgetTest0State state0 = tester.state(find.byType(WidgetTest0));
    expect(find.text('child 0'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 2', skipOffstage: false), findsNothing);

    childList = createSwitchedChildList(childList, 0, 2);
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    final _WidgetTest2State state2 = tester.state(find.byType(WidgetTest2));
    expect(find.text('child 2'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 0', skipOffstage: false), findsNothing);

    expect(state0.hasBeenDisposed, true);
    expect(state2.hasBeenDisposed, false);
  });

  testWidgets('Sliver with keep alive with key - should not dispose after reordering', (WidgetTester tester) async {
    List<Widget> childList= <Widget>[
      WidgetTest0(text: 'child 0', key: GlobalKey(), keepAlive: true),
      WidgetTest1(text: 'child 1', key: GlobalKey(), keepAlive: true),
      WidgetTest2(text: 'child 2', key: GlobalKey(), keepAlive: true),
    ];
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    final _WidgetTest0State state0 = tester.state(find.byType(WidgetTest0));
    expect(find.text('child 0'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 2', skipOffstage: false), findsNothing);

    childList = createSwitchedChildList(childList, 0, 2);
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    final _WidgetTest2State state2 = tester.state(find.byType(WidgetTest2));
    expect(find.text('child 2'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 0', skipOffstage: false), findsOneWidget);
    expect(state0.hasBeenDisposed, false);
    expect(state2.hasBeenDisposed, false);
  });

  testWidgets('Sliver with keep alive with Unique key - should not dispose after reordering', (WidgetTester tester) async {
    List<Widget> childList= <Widget>[
      WidgetTest0(text: 'child 0', key: UniqueKey(), keepAlive: true),
      WidgetTest1(text: 'child 1', key: UniqueKey(), keepAlive: true),
      WidgetTest2(text: 'child 2', key: UniqueKey(), keepAlive: true),
    ];
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    final _WidgetTest0State state0 = tester.state(find.byType(WidgetTest0));
    expect(find.text('child 0'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 2', skipOffstage: false), findsNothing);

    childList = createSwitchedChildList(childList, 0, 2);
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    final _WidgetTest2State state2 = tester.state(find.byType(WidgetTest2));
    expect(find.text('child 2'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 0', skipOffstage: false), findsOneWidget);
    expect(state0.hasBeenDisposed, false);
    expect(state2.hasBeenDisposed, false);
  });

  testWidgets('Sliver with keep alive with Value key - should not dispose after reordering', (WidgetTester tester) async {
    List<Widget> childList= <Widget>[
      const WidgetTest0(text: 'child 0', key: ValueKey<int>(0), keepAlive: true),
      const WidgetTest1(text: 'child 1', key: ValueKey<int>(1), keepAlive: true),
      const WidgetTest2(text: 'child 2', key: ValueKey<int>(2), keepAlive: true),
    ];
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    final _WidgetTest0State state0 = tester.state(find.byType(WidgetTest0));
    expect(find.text('child 0'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 2', skipOffstage: false), findsNothing);

    childList = createSwitchedChildList(childList, 0, 2);
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    final _WidgetTest2State state2 = tester.state(find.byType(WidgetTest2));
    expect(find.text('child 2'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 0', skipOffstage: false), findsOneWidget);
    expect(state0.hasBeenDisposed, false);
    expect(state2.hasBeenDisposed, false);
  });

  testWidgets('Sliver complex case 1', (WidgetTester tester) async {
    List<Widget> childList= <Widget>[
      WidgetTest0(text: 'child 0', key: GlobalKey(), keepAlive: true),
      WidgetTest1(text: 'child 1', key: GlobalKey(), keepAlive: true),
      const WidgetTest2(text: 'child 2', keepAlive: true),
    ];
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    final _WidgetTest0State state0 = tester.state(find.byType(WidgetTest0));
    expect(find.text('child 0'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 2', skipOffstage: false), findsNothing);

    childList = createSwitchedChildList(childList, 0, 2);
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    final _WidgetTest2State state2 = tester.state(find.byType(WidgetTest2));
    expect(find.text('child 2'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 0', skipOffstage: false), findsOneWidget);

    childList = createSwitchedChildList(childList, 0, 1);
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    final _WidgetTest1State state1 = tester.state(find.byType(WidgetTest1));
    expect(find.text('child 1'), findsOneWidget);
    expect(find.text('child 2', skipOffstage: false), findsNothing);
    expect(find.text('child 0', skipOffstage: false), findsOneWidget);

    childList = createSwitchedChildList(childList, 1, 2);
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    expect(find.text('child 1'), findsOneWidget);
    expect(find.text('child 0', skipOffstage: false), findsOneWidget);
    expect(find.text('child 2', skipOffstage: false), findsNothing);

    childList = createSwitchedChildList(childList, 0, 1);
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    expect(find.text('child 0'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsOneWidget);
    expect(find.text('child 2', skipOffstage: false), findsNothing);

    expect(state0.hasBeenDisposed, false);
    expect(state1.hasBeenDisposed, false);
    // Child 2 does not have a key.
    expect(state2.hasBeenDisposed, true);
  });

  testWidgets('Sliver complex case 2', (WidgetTester tester) async {
    List<Widget> childList= <Widget>[
      WidgetTest0(text: 'child 0', key: GlobalKey(), keepAlive: true),
      WidgetTest1(text: 'child 1', key: UniqueKey()),
      const WidgetTest2(text: 'child 2', keepAlive: true),
    ];
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    final _WidgetTest0State state0 = tester.state(find.byType(WidgetTest0));
    expect(find.text('child 0'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 2', skipOffstage: false), findsNothing);

    childList = createSwitchedChildList(childList, 0, 2);
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    final _WidgetTest2State state2 = tester.state(find.byType(WidgetTest2));
    expect(find.text('child 2'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 0', skipOffstage: false), findsOneWidget);

    childList = createSwitchedChildList(childList, 0, 1);
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    final _WidgetTest1State state1 = tester.state(find.byType(WidgetTest1));
    expect(find.text('child 1'), findsOneWidget);
    expect(find.text('child 2', skipOffstage: false), findsNothing);
    expect(find.text('child 0', skipOffstage: false), findsOneWidget);

    childList = createSwitchedChildList(childList, 1, 2);
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    expect(find.text('child 1'), findsOneWidget);
    expect(find.text('child 0', skipOffstage: false), findsOneWidget);
    expect(find.text('child 2', skipOffstage: false), findsNothing);

    childList = createSwitchedChildList(childList, 0, 1);
    await tester.pumpWidget(SwitchingChildListTest(children: childList));
    expect(find.text('child 0'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 2', skipOffstage: false), findsNothing);

    expect(state0.hasBeenDisposed, false);
    expect(state1.hasBeenDisposed, true);
    expect(state2.hasBeenDisposed, true);
  });

  testWidgets('Sliver with SliverChildBuilderDelegate', (WidgetTester tester) async {
    List<Widget> childList= <Widget>[
      WidgetTest0(text: 'child 0', key: UniqueKey(), keepAlive: true),
      WidgetTest1(text: 'child 1', key: GlobalKey()),
      const WidgetTest2(text: 'child 2', keepAlive: true),
    ];
    await tester.pumpWidget(SwitchingChildBuilderTest(children: childList));
    final _WidgetTest0State state0 = tester.state(find.byType(WidgetTest0));
    expect(find.text('child 0'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 2', skipOffstage: false), findsNothing);

    childList = createSwitchedChildList(childList, 0, 2);
    await tester.pumpWidget(SwitchingChildBuilderTest(children: childList));
    final _WidgetTest2State state2 = tester.state(find.byType(WidgetTest2));
    expect(find.text('child 2'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 0', skipOffstage: false), findsOneWidget);

    childList = createSwitchedChildList(childList, 0, 1);
    await tester.pumpWidget(SwitchingChildBuilderTest(children: childList));
    final _WidgetTest1State state1 = tester.state(find.byType(WidgetTest1));
    expect(find.text('child 1'), findsOneWidget);
    expect(find.text('child 2', skipOffstage: false), findsNothing);
    expect(find.text('child 0', skipOffstage: false), findsOneWidget);

    childList = createSwitchedChildList(childList, 1, 2);
    await tester.pumpWidget(SwitchingChildBuilderTest(children: childList));
    expect(find.text('child 1'), findsOneWidget);
    expect(find.text('child 0', skipOffstage: false), findsOneWidget);
    expect(find.text('child 2', skipOffstage: false), findsNothing);

    childList = createSwitchedChildList(childList, 0, 1);
    await tester.pumpWidget(SwitchingChildBuilderTest(children: childList));
    expect(find.text('child 0'), findsOneWidget);
    expect(find.text('child 1', skipOffstage: false), findsNothing);
    expect(find.text('child 2', skipOffstage: false), findsNothing);

    expect(state0.hasBeenDisposed, false);
    expect(state1.hasBeenDisposed, true);
    expect(state2.hasBeenDisposed, true);
  });

  testWidgets('SliverFillViewport should not dispose widget with key during in screen reordering', (WidgetTester tester) async {
    List<Widget> childList= <Widget>[
      WidgetTest0(text: 'child 0', key: UniqueKey(), keepAlive: true),
      WidgetTest1(text: 'child 1', key: UniqueKey()),
      const WidgetTest2(text: 'child 2', keepAlive: true),
    ];
    await tester.pumpWidget(
        SwitchingChildListTest(viewportFraction: 0.1, children: childList),
    );
    final _WidgetTest0State state0 = tester.state(find.byType(WidgetTest0));
    final _WidgetTest1State state1 = tester.state(find.byType(WidgetTest1));
    final _WidgetTest2State state2 = tester.state(find.byType(WidgetTest2));
    expect(find.text('child 0'), findsOneWidget);
    expect(find.text('child 1'), findsOneWidget);
    expect(find.text('child 2'), findsOneWidget);

    childList = createSwitchedChildList(childList, 0, 2);
    await tester.pumpWidget(
        SwitchingChildListTest(viewportFraction: 0.1, children: childList),
    );

    childList = createSwitchedChildList(childList, 0, 1);
    await tester.pumpWidget(
        SwitchingChildListTest(viewportFraction: 0.1, children: childList),
    );

    childList = createSwitchedChildList(childList, 1, 2);
    await tester.pumpWidget(
        SwitchingChildListTest(viewportFraction: 0.1, children: childList),
    );

    childList = createSwitchedChildList(childList, 0, 1);
    await tester.pumpWidget(
        SwitchingChildListTest(viewportFraction: 0.1, children: childList),
    );

    expect(state0.hasBeenDisposed, false);
    expect(state1.hasBeenDisposed, false);
    expect(state2.hasBeenDisposed, true);
  });

  testWidgets('SliverList should not dispose widget with key during in screen reordering', (WidgetTester tester) async {
    List<Widget> childList= <Widget>[
      WidgetTest0(text: 'child 0', key: UniqueKey(), keepAlive: true),
      const WidgetTest1(text: 'child 1', keepAlive: true),
      WidgetTest2(text: 'child 2', key: UniqueKey()),
    ];
    await tester.pumpWidget(
        SwitchingSliverListTest(viewportFraction: 0.1, children: childList),
    );
    final _WidgetTest0State state0 = tester.state(find.byType(WidgetTest0));
    final _WidgetTest1State state1 = tester.state(find.byType(WidgetTest1));
    final _WidgetTest2State state2 = tester.state(find.byType(WidgetTest2));
    expect(find.text('child 0'), findsOneWidget);
    expect(find.text('child 1'), findsOneWidget);
    expect(find.text('child 2'), findsOneWidget);

    childList = createSwitchedChildList(childList, 0, 2);
    await tester.pumpWidget(
        SwitchingSliverListTest(viewportFraction: 0.1, children: childList),
    );

    childList = createSwitchedChildList(childList, 1, 2);
    await tester.pumpWidget(
        SwitchingSliverListTest(viewportFraction: 0.1, children: childList),
    );

    childList = createSwitchedChildList(childList, 1, 2);
    await tester.pumpWidget(
        SwitchingSliverListTest(viewportFraction: 0.1, children: childList),
    );

    childList = createSwitchedChildList(childList, 0, 1);
    await tester.pumpWidget(
        SwitchingSliverListTest(viewportFraction: 0.1, children: childList),
    );

    childList = createSwitchedChildList(childList, 0, 2);
    await tester.pumpWidget(
        SwitchingSliverListTest(viewportFraction: 0.1, children: childList),
    );

    childList = createSwitchedChildList(childList, 0, 1);
    await tester.pumpWidget(
        SwitchingSliverListTest(viewportFraction: 0.1, children: childList),
    );
    expect(state0.hasBeenDisposed, false);
    expect(state1.hasBeenDisposed, true);
    expect(state2.hasBeenDisposed, false);
  });

  testWidgets('SliverList remove child from child list', (WidgetTester tester) async {
    List<Widget> childList= <Widget>[
      WidgetTest0(text: 'child 0', key: UniqueKey(), keepAlive: true),
      const WidgetTest1(text: 'child 1', keepAlive: true),
      WidgetTest2(text: 'child 2', key: UniqueKey()),
    ];
    await tester.pumpWidget(
        SwitchingSliverListTest(viewportFraction: 0.1, children: childList),
    );
    final _WidgetTest0State state0 = tester.state(find.byType(WidgetTest0));
    final _WidgetTest1State state1 = tester.state(find.byType(WidgetTest1));
    final _WidgetTest2State state2 = tester.state(find.byType(WidgetTest2));
    expect(find.text('child 0'), findsOneWidget);
    expect(find.text('child 1'), findsOneWidget);
    expect(find.text('child 2'), findsOneWidget);

    childList = createSwitchedChildList(childList, 0, 1);
    childList.removeAt(2);
    await tester.pumpWidget(
        SwitchingSliverListTest(viewportFraction: 0.1, children: childList),
    );
    expect(find.text('child 0'), findsOneWidget);
    expect(find.text('child 1'), findsOneWidget);
    expect(find.text('child 2'), findsNothing);
    expect(state0.hasBeenDisposed, false);
    expect(state1.hasBeenDisposed, true);
    expect(state2.hasBeenDisposed, true);
  });
}

List<Widget> createSwitchedChildList(List<Widget> childList, int i, int j) {
  final Widget w = childList[i];
  childList[i] = childList[j];
  childList[j] = w;
  return List<Widget>.from(childList);
}

class SwitchingChildBuilderTest extends StatefulWidget {
  const SwitchingChildBuilderTest({
    required this.children,
    super.key,
  });

  final List<Widget> children;

  @override
  State<SwitchingChildBuilderTest> createState() => _SwitchingChildBuilderTest();
}

class _SwitchingChildBuilderTest extends State<SwitchingChildBuilderTest> {
  late List<Widget> children;
  late Map<Key, int> _mapKeyToIndex;

  @override
  void initState() {
    super.initState();
    children = widget.children;
    _mapKeyToIndex = <Key, int>{};
    for (int index = 0; index < children.length; index += 1) {
      final Key? key = children[index].key;
      if (key != null) {
        _mapKeyToIndex[key] = index;
      }
    }
  }

  @override
  void didUpdateWidget(SwitchingChildBuilderTest oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.children != widget.children) {
      children = widget.children;
      _mapKeyToIndex = <Key, int>{};
      for (int index = 0; index < children.length; index += 1) {
        final Key? key = children[index].key;
        if (key != null) {
          _mapKeyToIndex[key] = index;
        }
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Directionality(
      textDirection: TextDirection.ltr,
      child: Center(
        child: SizedBox(
          height: 100,
          child: CustomScrollView(
            cacheExtent: 0,
            slivers: <Widget>[
              SliverFillViewport(
                delegate: SliverChildBuilderDelegate(
                  (BuildContext context, int index) {
                    return children[index];
                  },
                  childCount: children.length,
                  findChildIndexCallback: (Key key) => _mapKeyToIndex[key] ?? -1,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class SwitchingChildListTest extends StatelessWidget {
  const SwitchingChildListTest({
    required this.children,
    this.viewportFraction = 1.0,
    super.key,
  });

  final List<Widget> children;
  final double viewportFraction;

  @override
  Widget build(BuildContext context) {
    return Directionality(
      textDirection: TextDirection.ltr,
      child: Center(
        child: SizedBox(
          height: 100,
          child: CustomScrollView(
            cacheExtent: 0,
            slivers: <Widget>[
              SliverFillViewport(
                viewportFraction: viewportFraction,
                delegate: SliverChildListDelegate(children),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class SwitchingSliverListTest extends StatelessWidget {
  const SwitchingSliverListTest({
    required this.children,
    this.viewportFraction = 1.0,
    super.key,
  });

  final List<Widget> children;
  final double viewportFraction;

  @override
  Widget build(BuildContext context) {
    return Directionality(
      textDirection: TextDirection.ltr,
      child: Center(
        child: SizedBox(
          height: 100,
          child: CustomScrollView(
            cacheExtent: 0,
            slivers: <Widget>[
              SliverList(
                delegate: SliverChildListDelegate(children),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class WidgetTest0 extends StatefulWidget {
  const WidgetTest0({
    required this.text,
    this.keepAlive = false,
    super.key,
  });

  final String text;
  final bool keepAlive;

  @override
  State<WidgetTest0> createState() => _WidgetTest0State();
}

class _WidgetTest0State extends State<WidgetTest0> with AutomaticKeepAliveClientMixin {
  bool hasBeenDisposed = false;

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Text(widget.text);
  }

  @override
  void dispose() {
    hasBeenDisposed = true;
    super.dispose();
  }

  @override
  bool get wantKeepAlive => widget.keepAlive;
}

class WidgetTest1 extends StatefulWidget {
  const WidgetTest1({
    required this.text,
    this.keepAlive = false,
    super.key,
  });

  final String text;
  final bool keepAlive;

  @override
  State<WidgetTest1> createState() => _WidgetTest1State();
}

class _WidgetTest1State extends State<WidgetTest1> with AutomaticKeepAliveClientMixin {
  bool hasBeenDisposed = false;

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Text(widget.text);
  }

  @override
  void dispose() {
    hasBeenDisposed = true;
    super.dispose();
  }

  @override
  bool get wantKeepAlive => widget.keepAlive;
}

class WidgetTest2 extends StatefulWidget {
  const WidgetTest2({
    required this.text,
    this.keepAlive = false,
    super.key,
  });

  final String text;
  final bool keepAlive;

  @override
  State<WidgetTest2> createState() => _WidgetTest2State();
}

class _WidgetTest2State extends State<WidgetTest2> with AutomaticKeepAliveClientMixin {
  bool hasBeenDisposed = false;

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Text(widget.text);
  }

  @override
  void dispose() {
    hasBeenDisposed = true;
    super.dispose();
  }

  @override
  bool get wantKeepAlive => widget.keepAlive;
}