mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
214 lines
6.0 KiB
Dart
214 lines
6.0 KiB
Dart
// Copyright 2016 The Chromium 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/material.dart';
|
|
|
|
class NavigationIconView {
|
|
NavigationIconView({
|
|
Widget icon,
|
|
Widget title,
|
|
Color color,
|
|
TickerProvider vsync,
|
|
}) : _icon = icon,
|
|
_color = color,
|
|
item = new BottomNavigationBarItem(
|
|
icon: icon,
|
|
title: title,
|
|
backgroundColor: color,
|
|
),
|
|
controller = new AnimationController(
|
|
duration: kThemeAnimationDuration,
|
|
vsync: vsync,
|
|
) {
|
|
_animation = new CurvedAnimation(
|
|
parent: controller,
|
|
curve: const Interval(0.5, 1.0, curve: Curves.fastOutSlowIn),
|
|
);
|
|
}
|
|
|
|
final Widget _icon;
|
|
final Color _color;
|
|
final BottomNavigationBarItem item;
|
|
final AnimationController controller;
|
|
CurvedAnimation _animation;
|
|
|
|
FadeTransition transition(BottomNavigationBarType type, BuildContext context) {
|
|
Color iconColor;
|
|
if (type == BottomNavigationBarType.shifting) {
|
|
iconColor = _color;
|
|
} else {
|
|
final ThemeData themeData = Theme.of(context);
|
|
iconColor = themeData.brightness == Brightness.light
|
|
? themeData.primaryColor
|
|
: themeData.accentColor;
|
|
}
|
|
|
|
return new FadeTransition(
|
|
opacity: _animation,
|
|
child: new SlideTransition(
|
|
position: new Tween<FractionalOffset>(
|
|
begin: const FractionalOffset(0.0, 0.02), // Small offset from the top.
|
|
end: FractionalOffset.topLeft,
|
|
).animate(_animation),
|
|
child: new IconTheme(
|
|
data: new IconThemeData(
|
|
color: iconColor,
|
|
size: 120.0,
|
|
),
|
|
child: _icon,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class CustomIcon extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final IconThemeData iconTheme = IconTheme.of(context);
|
|
return new Container(
|
|
margin: const EdgeInsets.all(4.0),
|
|
width: iconTheme.size - 8.0,
|
|
height: iconTheme.size - 8.0,
|
|
decoration: new BoxDecoration(
|
|
backgroundColor: iconTheme.color,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class BottomNavigationDemo extends StatefulWidget {
|
|
static const String routeName = '/material/bottom_navigation';
|
|
|
|
@override
|
|
_BottomNavigationDemoState createState() => new _BottomNavigationDemoState();
|
|
}
|
|
|
|
class _BottomNavigationDemoState extends State<BottomNavigationDemo>
|
|
with TickerProviderStateMixin {
|
|
int _currentIndex = 0;
|
|
BottomNavigationBarType _type = BottomNavigationBarType.shifting;
|
|
List<NavigationIconView> _navigationViews;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_navigationViews = <NavigationIconView>[
|
|
new NavigationIconView(
|
|
icon: const Icon(Icons.access_alarm),
|
|
title: const Text('Alarm'),
|
|
color: Colors.deepPurple,
|
|
vsync: this,
|
|
),
|
|
new NavigationIconView(
|
|
icon: new CustomIcon(),
|
|
title: const Text('Box'),
|
|
color: Colors.deepOrange,
|
|
vsync: this,
|
|
),
|
|
new NavigationIconView(
|
|
icon: const Icon(Icons.cloud),
|
|
title: const Text('Cloud'),
|
|
color: Colors.teal,
|
|
vsync: this,
|
|
),
|
|
new NavigationIconView(
|
|
icon: const Icon(Icons.favorite),
|
|
title: const Text('Favorites'),
|
|
color: Colors.indigo,
|
|
vsync: this,
|
|
),
|
|
new NavigationIconView(
|
|
icon: const Icon(Icons.event_available),
|
|
title: const Text('Event'),
|
|
color: Colors.pink,
|
|
vsync: this,
|
|
)
|
|
];
|
|
|
|
for (NavigationIconView view in _navigationViews)
|
|
view.controller.addListener(_rebuild);
|
|
|
|
_navigationViews[_currentIndex].controller.value = 1.0;
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
for (NavigationIconView view in _navigationViews)
|
|
view.controller.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
void _rebuild() {
|
|
setState(() {
|
|
// Rebuild in order to animate views.
|
|
});
|
|
}
|
|
|
|
Widget _buildTransitionsStack() {
|
|
final List<FadeTransition> transitions = <FadeTransition>[];
|
|
|
|
for (NavigationIconView view in _navigationViews)
|
|
transitions.add(view.transition(_type, context));
|
|
|
|
// We want to have the newly animating (fading in) views on top.
|
|
transitions.sort((FadeTransition a, FadeTransition b) {
|
|
final Animation<double> aAnimation = a.listenable;
|
|
final Animation<double> bAnimation = b.listenable;
|
|
final double aValue = aAnimation.value;
|
|
final double bValue = bAnimation.value;
|
|
return aValue.compareTo(bValue);
|
|
});
|
|
|
|
return new Stack(children: transitions);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final BottomNavigationBar botNavBar = new BottomNavigationBar(
|
|
items: _navigationViews
|
|
.map((NavigationIconView navigationView) => navigationView.item)
|
|
.toList(),
|
|
currentIndex: _currentIndex,
|
|
type: _type,
|
|
onTap: (int index) {
|
|
setState(() {
|
|
_navigationViews[_currentIndex].controller.reverse();
|
|
_currentIndex = index;
|
|
_navigationViews[_currentIndex].controller.forward();
|
|
});
|
|
},
|
|
);
|
|
|
|
return new Scaffold(
|
|
appBar: new AppBar(
|
|
title: const Text('Bottom navigation'),
|
|
actions: <Widget>[
|
|
new PopupMenuButton<BottomNavigationBarType>(
|
|
onSelected: (BottomNavigationBarType value) {
|
|
setState(() {
|
|
_type = value;
|
|
});
|
|
},
|
|
itemBuilder: (BuildContext context) => <PopupMenuItem<BottomNavigationBarType>>[
|
|
new PopupMenuItem<BottomNavigationBarType>(
|
|
value: BottomNavigationBarType.fixed,
|
|
child: const Text('Fixed'),
|
|
),
|
|
new PopupMenuItem<BottomNavigationBarType>(
|
|
value: BottomNavigationBarType.shifting,
|
|
child: const Text('Shifting'),
|
|
)
|
|
],
|
|
)
|
|
],
|
|
),
|
|
body: new Center(
|
|
child: _buildTransitionsStack()
|
|
),
|
|
bottomNavigationBar: botNavBar,
|
|
);
|
|
}
|
|
}
|