mirror of
https://github.com/flutter/flutter
synced 2024-10-14 04:02:56 +00:00
4955eef8a7
Now IconThemeData.fallback is a factory constructor and IconThemeData.of() does the work of computing the fallback for its clients. Also, add tests for ImageIcon and ListItems.
212 lines
5.9 KiB
Dart
212 lines
5.9 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: new 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 = '/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: new Icon(Icons.access_alarm),
|
|
title: new Text('Alarm'),
|
|
color: Colors.deepPurple[500],
|
|
vsync: this,
|
|
),
|
|
new NavigationIconView(
|
|
icon: new CustomIcon(),
|
|
title: new Text('Box'),
|
|
color: Colors.deepOrange[500],
|
|
vsync: this,
|
|
),
|
|
new NavigationIconView(
|
|
icon: new Icon(Icons.cloud),
|
|
title: new Text('Cloud'),
|
|
color: Colors.teal[500],
|
|
vsync: this,
|
|
),
|
|
new NavigationIconView(
|
|
icon: new Icon(Icons.favorite),
|
|
title: new Text('Favorites'),
|
|
color: Colors.indigo[500],
|
|
vsync: this,
|
|
),
|
|
new NavigationIconView(
|
|
icon: new Icon(Icons.event_available),
|
|
title: new Text('Event'),
|
|
color: Colors.pink[500],
|
|
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) {
|
|
double aValue = a.animation.value;
|
|
double bValue = b.animation.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: new 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: new Text('Fixed'),
|
|
),
|
|
new PopupMenuItem<BottomNavigationBarType>(
|
|
value: BottomNavigationBarType.shifting,
|
|
child: new Text('Shifting'),
|
|
)
|
|
],
|
|
)
|
|
],
|
|
),
|
|
body: new Center(
|
|
child: _buildTransitionsStack()
|
|
),
|
|
bottomNavigationBar: botNavBar,
|
|
);
|
|
}
|
|
}
|