diff --git a/web/slide_puzzle/lib/src/puzzle_home_state.dart b/web/slide_puzzle/lib/src/puzzle_home_state.dart index c740d98b1..5d8c24c59 100644 --- a/web/slide_puzzle/lib/src/puzzle_home_state.dart +++ b/web/slide_puzzle/lib/src/puzzle_home_state.dart @@ -12,6 +12,7 @@ import 'flutter.dart'; import 'puzzle_flow_delegate.dart'; import 'shared_theme.dart'; import 'themes.dart'; +import 'value_tab_controller.dart'; class PuzzleHomeState extends State with TickerProviderStateMixin, AppState { @override @@ -163,7 +164,8 @@ Widget _updateConstraints( Widget _doBuild(BuildContext _, BoxConstraints constraints) => _updateConstraints(constraints, _doBuildCore); -Widget _doBuildCore(bool small) => PuzzleThemeTabController( +Widget _doBuildCore(bool small) => ValueTabController( + values: themes, child: Consumer( builder: (_, theme, __) => AnimatedContainer( duration: puzzleAnimationDuration, @@ -190,8 +192,7 @@ Widget _doBuildCore(bool small) => PuzzleThemeTabController( margin: const EdgeInsets.symmetric(horizontal: 20), child: TabBar( - controller: - PuzzleThemeTabController.of(context), + controller: ValueTabController.of(context), labelPadding: const EdgeInsets.fromLTRB(0, 20, 0, 12), labelColor: theme.puzzleAccentColor, diff --git a/web/slide_puzzle/lib/src/themes.dart b/web/slide_puzzle/lib/src/themes.dart index e4309ad33..7889db6c3 100644 --- a/web/slide_puzzle/lib/src/themes.dart +++ b/web/slide_puzzle/lib/src/themes.dart @@ -1,7 +1,3 @@ -import 'package:flutter_web/material.dart'; -import 'package:provider/provider.dart'; - -import 'shared_theme.dart'; import 'theme_plaster.dart'; import 'theme_seattle.dart'; import 'theme_simple.dart'; @@ -11,86 +7,3 @@ const themes = [ ThemeSeattle(), ThemePlaster(), ]; - -class PuzzleThemeTabController extends StatefulWidget { - /// Creates a default tab controller for the given [child] widget. - const PuzzleThemeTabController({ - Key key, - @required this.child, - }) : super(key: key); - - /// The widget below this widget in the tree. - /// - /// Typically a [Scaffold] whose [AppBar] includes a [TabBar]. - /// - /// {@macro flutter.widgets.child} - final Widget child; - - /// The closest instance of this class that encloses the given context. - /// - /// Typical usage: - /// - /// ```dart - /// TabController controller = DefaultTabBarController.of(context); - /// ``` - static TabController of(BuildContext context) { - final scope = - context.inheritFromWidgetOfExactType(_PuzzleThemeTabControllerScope) - as _PuzzleThemeTabControllerScope; - return scope?.controller; - } - - @override - _PuzzleThemeTabControllerState createState() => - _PuzzleThemeTabControllerState(); -} - -class _PuzzleThemeTabControllerState extends State - with SingleTickerProviderStateMixin { - final _notifier = ValueNotifier(themes.first); - - TabController _controller; - - @override - void initState() { - super.initState(); - _controller = TabController( - vsync: this, - length: themes.length, - initialIndex: 0, - ); - - _controller.addListener(() { - _notifier.value = themes[_controller.index]; - }); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) => _PuzzleThemeTabControllerScope( - controller: _controller, - enabled: TickerMode.of(context), - child: ValueListenableProvider.value( - valueListenable: _notifier, - child: widget.child, - ), - ); -} - -class _PuzzleThemeTabControllerScope extends InheritedWidget { - const _PuzzleThemeTabControllerScope( - {Key key, this.controller, this.enabled, Widget child}) - : super(key: key, child: child); - - final TabController controller; - final bool enabled; - - @override - bool updateShouldNotify(_PuzzleThemeTabControllerScope old) => - enabled != old.enabled || controller != old.controller; -} diff --git a/web/slide_puzzle/lib/src/value_tab_controller.dart b/web/slide_puzzle/lib/src/value_tab_controller.dart new file mode 100644 index 000000000..095c29ca6 --- /dev/null +++ b/web/slide_puzzle/lib/src/value_tab_controller.dart @@ -0,0 +1,88 @@ +import 'package:flutter_web/material.dart'; +import 'package:provider/provider.dart'; + +class ValueTabController extends StatefulWidget { + /// Creates a default tab controller for the given [child] widget. + const ValueTabController({ + Key key, + @required this.child, + @required this.values, + }) : super(key: key); + + /// The widget below this widget in the tree. + /// + /// Typically a [Scaffold] whose [AppBar] includes a [TabBar]. + /// + /// {@macro flutter.widgets.child} + final Widget child; + + final List values; + + /// The closest instance of this class that encloses the given context. + /// + /// Typical usage: + /// + /// ```dart + /// TabController controller = DefaultTabBarController.of(context); + /// ``` + static TabController of(BuildContext context) { + final scope = context.inheritFromWidgetOfExactType(_ValueTabControllerScope) + as _ValueTabControllerScope; + return scope?.controller; + } + + @override + _ValueTabControllerState createState() => _ValueTabControllerState(); +} + +class _ValueTabControllerState extends State> + with SingleTickerProviderStateMixin { + final _notifier = ValueNotifier(null); + + TabController _controller; + + @override + void initState() { + super.initState(); + _controller = TabController( + vsync: this, + length: widget.values.length, + initialIndex: 0, + ); + + _notifier.value = widget.values.first; + + _controller.addListener(() { + _notifier.value = widget.values[_controller.index]; + }); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) => _ValueTabControllerScope( + controller: _controller, + enabled: TickerMode.of(context), + child: ValueListenableProvider.value( + valueListenable: _notifier, + child: widget.child, + ), + ); +} + +class _ValueTabControllerScope extends InheritedWidget { + const _ValueTabControllerScope( + {Key key, this.controller, this.enabled, Widget child}) + : super(key: key, child: child); + + final TabController controller; + final bool enabled; + + @override + bool updateShouldNotify(_ValueTabControllerScope old) => + enabled != old.enabled || controller != old.controller; +}