Created
June 17, 2019 19:04
-
-
Save HansMuller/b189642d10fd236a41044fdf7626f7b0 to your computer and use it in GitHub Desktop.
Revisions
-
Hans Muller created this gist
Jun 17, 2019 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,292 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; class Destination { const Destination(this.index, this.title, this.icon, this.color); final int index; final String title; final IconData icon; final MaterialColor color; } const List<Destination> allDestinations = <Destination>[ Destination(0, 'Home', Icons.home, Colors.teal), Destination(1, 'Business', Icons.business, Colors.cyan), Destination(2, 'School', Icons.school, Colors.orange), Destination(3, 'Flight', Icons.flight, Colors.blue) ]; class RootPage extends StatelessWidget { const RootPage({ Key key, this.destination }) : super(key: key); final Destination destination; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(destination.title), backgroundColor: destination.color, ), backgroundColor: destination.color[50], body: SizedBox.expand( child: InkWell( onTap: () { Navigator.pushNamed(context, "/list"); }, child: Center( child: Text('tap here'), ), ), ), ); } } class ListPage extends StatelessWidget { const ListPage({ Key key, this.destination }) : super(key: key); final Destination destination; @override Widget build(BuildContext context) { const List<int> shades = <int>[50, 100, 200, 300, 400, 500, 600, 700, 800, 900]; return Scaffold( appBar: AppBar( title: Text(destination.title), backgroundColor: destination.color, ), backgroundColor: destination.color[50], body: SizedBox.expand( child: ListView.builder( itemCount: shades.length, itemBuilder: (BuildContext context, int index) { return SizedBox( height: 128, child: Card( color: destination.color[shades[index]].withOpacity(0.25), child: InkWell( onTap: () { Navigator.pushNamed(context, "/text"); }, child: Center( child: Text('Item $index', style: Theme.of(context).primaryTextTheme.display1), ), ), ), ); }, ), ), ); } } class TextPage extends StatefulWidget { const TextPage({ Key key, this.destination }) : super(key: key); final Destination destination; @override _TextPageState createState() => _TextPageState(); } class _TextPageState extends State<TextPage> { TextEditingController _textController; @override void initState() { super.initState(); _textController = TextEditingController( text: 'sample text: ${widget.destination.title}', ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.destination.title), backgroundColor: widget.destination.color, ), backgroundColor: widget.destination.color[50], body: Container( padding: const EdgeInsets.all(32.0), alignment: Alignment.center, child: TextField(controller: _textController), ), ); } @override void dispose() { _textController.dispose(); super.dispose(); } } class ViewNavigatorObserver extends NavigatorObserver { ViewNavigatorObserver(this.onNavigation); final VoidCallback onNavigation; void didPop(Route<dynamic> route, Route<dynamic> previousRoute) { onNavigation(); } void didPush(Route<dynamic> route, Route<dynamic> previousRoute) { onNavigation(); } } class DestinationView extends StatefulWidget { const DestinationView({ Key key, this.destination, this.onNavigation }) : super(key: key); final Destination destination; final VoidCallback onNavigation; @override _DestinationViewState createState() => _DestinationViewState(); } class _DestinationViewState extends State<DestinationView> { @override Widget build(BuildContext context) { return Navigator( observers: <NavigatorObserver>[ ViewNavigatorObserver(widget.onNavigation), ], onGenerateRoute: (RouteSettings settings) { return MaterialPageRoute( settings: settings, builder: (BuildContext context) { switch(settings.name) { case '/': return RootPage(destination: widget.destination); case '/list': return ListPage(destination: widget.destination); case '/text': return TextPage(destination: widget.destination); } }, ); }, ); } } class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> with TickerProviderStateMixin<HomePage> { List<Key> _destinationKeys; List<AnimationController> _faders; AnimationController _hide; int _currentIndex = 0; @override void initState() { super.initState(); _faders = allDestinations.map<AnimationController>((Destination destination) { return AnimationController(vsync: this, duration: Duration(milliseconds: 200)); }).toList(); _faders[_currentIndex].value = 1.0; _destinationKeys = List<Key>.generate(allDestinations.length, (int index) => GlobalKey()).toList(); _hide = AnimationController(vsync: this, duration: kThemeAnimationDuration); } @override void dispose() { for (AnimationController controller in _faders) controller.dispose(); _hide.dispose(); super.dispose(); } bool _handleScrollNotification(ScrollNotification notification) { if (notification.depth == 0) { if (notification is UserScrollNotification) { final UserScrollNotification userScroll = notification; switch (userScroll.direction) { case ScrollDirection.forward: _hide.forward(); break; case ScrollDirection.reverse: _hide.reverse(); break; case ScrollDirection.idle: break; } } } return false; } @override Widget build(BuildContext context) { return NotificationListener<ScrollNotification>( onNotification: _handleScrollNotification, child: Scaffold( body: SafeArea( top: false, child: Stack( fit: StackFit.expand, children: allDestinations.map((Destination destination) { final Widget view = FadeTransition( opacity: _faders[destination.index].drive(CurveTween(curve: Curves.fastOutSlowIn)), child: KeyedSubtree( key: _destinationKeys[destination.index], child: DestinationView( destination: destination, onNavigation: () { _hide.forward(); }, ), ), ); if (destination.index == _currentIndex) { _faders[destination.index].forward(); return view; } else { _faders[destination.index].reverse(); if (_faders[destination.index].isAnimating) { return IgnorePointer(child: view); } return Offstage(child: view); } }).toList(), ), ), bottomNavigationBar: ClipRect( child: SizeTransition( sizeFactor: _hide, axisAlignment: -1.0, child: BottomNavigationBar( currentIndex: _currentIndex, onTap: (int index) { setState(() { _currentIndex = index; }); }, items: allDestinations.map((Destination destination) { return BottomNavigationBarItem( icon: Icon(destination.icon), backgroundColor: destination.color, title: Text(destination.title) ); }).toList(), ), ), ), ), ); } } void main() { runApp(MaterialApp(home: HomePage())); }