• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2016 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'package:flutter/material.dart';
6
7import '../../gallery/demo.dart';
8
9class NavigationIconView {
10  NavigationIconView({
11    Widget icon,
12    Widget activeIcon,
13    String title,
14    Color color,
15    TickerProvider vsync,
16  }) : _icon = icon,
17       _color = color,
18       _title = title,
19       item = BottomNavigationBarItem(
20         icon: icon,
21         activeIcon: activeIcon,
22         title: Text(title),
23         backgroundColor: color,
24       ),
25       controller = AnimationController(
26         duration: kThemeAnimationDuration,
27         vsync: vsync,
28       ) {
29    _animation = controller.drive(CurveTween(
30      curve: const Interval(0.5, 1.0, curve: Curves.fastOutSlowIn),
31    ));
32  }
33
34  final Widget _icon;
35  final Color _color;
36  final String _title;
37  final BottomNavigationBarItem item;
38  final AnimationController controller;
39  Animation<double> _animation;
40
41  FadeTransition transition(BottomNavigationBarType type, BuildContext context) {
42    Color iconColor;
43    if (type == BottomNavigationBarType.shifting) {
44      iconColor = _color;
45    } else {
46      final ThemeData themeData = Theme.of(context);
47      iconColor = themeData.brightness == Brightness.light
48          ? themeData.primaryColor
49          : themeData.accentColor;
50    }
51
52    return FadeTransition(
53      opacity: _animation,
54      child: SlideTransition(
55        position: _animation.drive(
56          Tween<Offset>(
57            begin: const Offset(0.0, 0.02), // Slightly down.
58            end: Offset.zero,
59          ),
60        ),
61        child: IconTheme(
62          data: IconThemeData(
63            color: iconColor,
64            size: 120.0,
65          ),
66          child: Semantics(
67            label: 'Placeholder for $_title tab',
68            child: _icon,
69          ),
70        ),
71      ),
72    );
73  }
74}
75
76class CustomIcon extends StatelessWidget {
77  @override
78  Widget build(BuildContext context) {
79    final IconThemeData iconTheme = IconTheme.of(context);
80    return Container(
81      margin: const EdgeInsets.all(4.0),
82      width: iconTheme.size - 8.0,
83      height: iconTheme.size - 8.0,
84      color: iconTheme.color,
85    );
86  }
87}
88
89class CustomInactiveIcon extends StatelessWidget {
90  @override
91  Widget build(BuildContext context) {
92    final IconThemeData iconTheme = IconTheme.of(context);
93    return Container(
94      margin: const EdgeInsets.all(4.0),
95      width: iconTheme.size - 8.0,
96      height: iconTheme.size - 8.0,
97      decoration: BoxDecoration(
98        border: Border.all(color: iconTheme.color, width: 2.0),
99      ),
100    );
101  }
102}
103
104class BottomNavigationDemo extends StatefulWidget {
105  static const String routeName = '/material/bottom_navigation';
106
107  @override
108  _BottomNavigationDemoState createState() => _BottomNavigationDemoState();
109}
110
111class _BottomNavigationDemoState extends State<BottomNavigationDemo>
112    with TickerProviderStateMixin {
113  int _currentIndex = 0;
114  BottomNavigationBarType _type = BottomNavigationBarType.shifting;
115  List<NavigationIconView> _navigationViews;
116
117  @override
118  void initState() {
119    super.initState();
120    _navigationViews = <NavigationIconView>[
121      NavigationIconView(
122        icon: const Icon(Icons.access_alarm),
123        title: 'Alarm',
124        color: Colors.deepPurple,
125        vsync: this,
126      ),
127      NavigationIconView(
128        activeIcon: CustomIcon(),
129        icon: CustomInactiveIcon(),
130        title: 'Box',
131        color: Colors.deepOrange,
132        vsync: this,
133      ),
134      NavigationIconView(
135        activeIcon: const Icon(Icons.cloud),
136        icon: const Icon(Icons.cloud_queue),
137        title: 'Cloud',
138        color: Colors.teal,
139        vsync: this,
140      ),
141      NavigationIconView(
142        activeIcon: const Icon(Icons.favorite),
143        icon: const Icon(Icons.favorite_border),
144        title: 'Favorites',
145        color: Colors.indigo,
146        vsync: this,
147      ),
148      NavigationIconView(
149        icon: const Icon(Icons.event_available),
150        title: 'Event',
151        color: Colors.pink,
152        vsync: this,
153      ),
154    ];
155
156    _navigationViews[_currentIndex].controller.value = 1.0;
157  }
158
159  @override
160  void dispose() {
161    for (NavigationIconView view in _navigationViews)
162      view.controller.dispose();
163    super.dispose();
164  }
165
166  Widget _buildTransitionsStack() {
167    final List<FadeTransition> transitions = <FadeTransition>[];
168
169    for (NavigationIconView view in _navigationViews)
170      transitions.add(view.transition(_type, context));
171
172    // We want to have the newly animating (fading in) views on top.
173    transitions.sort((FadeTransition a, FadeTransition b) {
174      final Animation<double> aAnimation = a.opacity;
175      final Animation<double> bAnimation = b.opacity;
176      final double aValue = aAnimation.value;
177      final double bValue = bAnimation.value;
178      return aValue.compareTo(bValue);
179    });
180
181    return Stack(children: transitions);
182  }
183
184  @override
185  Widget build(BuildContext context) {
186    final BottomNavigationBar botNavBar = BottomNavigationBar(
187      items: _navigationViews
188          .map<BottomNavigationBarItem>((NavigationIconView navigationView) => navigationView.item)
189          .toList(),
190      currentIndex: _currentIndex,
191      type: _type,
192      onTap: (int index) {
193        setState(() {
194          _navigationViews[_currentIndex].controller.reverse();
195          _currentIndex = index;
196          _navigationViews[_currentIndex].controller.forward();
197        });
198      },
199    );
200
201    return Scaffold(
202      appBar: AppBar(
203        title: const Text('Bottom navigation'),
204        actions: <Widget>[
205          MaterialDemoDocumentationButton(BottomNavigationDemo.routeName),
206          PopupMenuButton<BottomNavigationBarType>(
207            onSelected: (BottomNavigationBarType value) {
208              setState(() {
209                _type = value;
210              });
211            },
212            itemBuilder: (BuildContext context) => <PopupMenuItem<BottomNavigationBarType>>[
213              const PopupMenuItem<BottomNavigationBarType>(
214                value: BottomNavigationBarType.fixed,
215                child: Text('Fixed'),
216              ),
217              const PopupMenuItem<BottomNavigationBarType>(
218                value: BottomNavigationBarType.shifting,
219                child: Text('Shifting'),
220              ),
221            ],
222          ),
223        ],
224      ),
225      body: Center(
226        child: _buildTransitionsStack(),
227      ),
228      bottomNavigationBar: botNavBar,
229    );
230  }
231}
232