mirror of https://github.com/flutter/samples.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
240 lines
6.7 KiB
240 lines
6.7 KiB
// Copyright 2018 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_web/material.dart';
|
|
|
|
import '../../gallery/demo.dart';
|
|
|
|
class NavigationIconView {
|
|
NavigationIconView({
|
|
Widget icon,
|
|
Widget activeIcon,
|
|
String title,
|
|
Color color,
|
|
TickerProvider vsync,
|
|
}) : _icon = icon,
|
|
_color = color,
|
|
_title = title,
|
|
item = BottomNavigationBarItem(
|
|
icon: icon,
|
|
activeIcon: activeIcon,
|
|
title: Text(title),
|
|
backgroundColor: color,
|
|
),
|
|
controller = AnimationController(
|
|
duration: kThemeAnimationDuration,
|
|
vsync: vsync,
|
|
) {
|
|
_animation = controller.drive(CurveTween(
|
|
curve: const Interval(0.5, 1.0, curve: Curves.fastOutSlowIn),
|
|
));
|
|
}
|
|
|
|
final Widget _icon;
|
|
final Color _color;
|
|
final String _title;
|
|
final BottomNavigationBarItem item;
|
|
final AnimationController controller;
|
|
Animation<double> _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 FadeTransition(
|
|
opacity: _animation,
|
|
child: SlideTransition(
|
|
position: _animation.drive(
|
|
Tween<Offset>(
|
|
begin: const Offset(0.0, 0.02), // Slightly down.
|
|
end: Offset.zero,
|
|
),
|
|
),
|
|
child: IconTheme(
|
|
data: IconThemeData(
|
|
color: iconColor,
|
|
size: 120.0,
|
|
),
|
|
child: Semantics(
|
|
label: 'Placeholder for $_title tab',
|
|
child: _icon,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class CustomIcon extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final IconThemeData iconTheme = IconTheme.of(context);
|
|
return Container(
|
|
margin: const EdgeInsets.all(4.0),
|
|
width: iconTheme.size - 8.0,
|
|
height: iconTheme.size - 8.0,
|
|
color: iconTheme.color,
|
|
);
|
|
}
|
|
}
|
|
|
|
class CustomInactiveIcon extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final IconThemeData iconTheme = IconTheme.of(context);
|
|
return Container(
|
|
margin: const EdgeInsets.all(4.0),
|
|
width: iconTheme.size - 8.0,
|
|
height: iconTheme.size - 8.0,
|
|
decoration: BoxDecoration(
|
|
border: Border.all(color: iconTheme.color, width: 2.0),
|
|
));
|
|
}
|
|
}
|
|
|
|
class BottomNavigationDemo extends StatefulWidget {
|
|
static const String routeName = '/material/bottom_navigation';
|
|
|
|
@override
|
|
_BottomNavigationDemoState createState() => _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>[
|
|
NavigationIconView(
|
|
icon: const Icon(Icons.access_alarm),
|
|
title: 'Alarm',
|
|
color: Colors.deepPurple,
|
|
vsync: this,
|
|
),
|
|
NavigationIconView(
|
|
activeIcon: CustomIcon(),
|
|
icon: CustomInactiveIcon(),
|
|
title: 'Box',
|
|
color: Colors.deepOrange,
|
|
vsync: this,
|
|
),
|
|
NavigationIconView(
|
|
activeIcon: const Icon(Icons.cloud),
|
|
icon: const Icon(Icons.cloud_queue),
|
|
title: 'Cloud',
|
|
color: Colors.teal,
|
|
vsync: this,
|
|
),
|
|
NavigationIconView(
|
|
activeIcon: const Icon(Icons.favorite),
|
|
icon: const Icon(Icons.favorite_border),
|
|
title: 'Favorites',
|
|
color: Colors.indigo,
|
|
vsync: this,
|
|
),
|
|
NavigationIconView(
|
|
icon: const Icon(Icons.event_available),
|
|
title: '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.opacity;
|
|
final Animation<double> bAnimation = b.opacity;
|
|
final double aValue = aAnimation.value;
|
|
final double bValue = bAnimation.value;
|
|
return aValue.compareTo(bValue);
|
|
});
|
|
|
|
return Stack(children: transitions);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final BottomNavigationBar botNavBar = BottomNavigationBar(
|
|
items: _navigationViews
|
|
.map<BottomNavigationBarItem>(
|
|
(NavigationIconView navigationView) => navigationView.item)
|
|
.toList(),
|
|
currentIndex: _currentIndex,
|
|
type: _type,
|
|
onTap: (int index) {
|
|
setState(() {
|
|
_navigationViews[_currentIndex].controller.reverse();
|
|
_currentIndex = index;
|
|
_navigationViews[_currentIndex].controller.forward();
|
|
});
|
|
},
|
|
);
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('Bottom navigation'),
|
|
actions: <Widget>[
|
|
MaterialDemoDocumentationButton(BottomNavigationDemo.routeName),
|
|
PopupMenuButton<BottomNavigationBarType>(
|
|
onSelected: (BottomNavigationBarType value) {
|
|
setState(() {
|
|
_type = value;
|
|
});
|
|
},
|
|
itemBuilder: (BuildContext context) =>
|
|
<PopupMenuItem<BottomNavigationBarType>>[
|
|
const PopupMenuItem<BottomNavigationBarType>(
|
|
value: BottomNavigationBarType.fixed,
|
|
child: Text('Fixed'),
|
|
),
|
|
const PopupMenuItem<BottomNavigationBarType>(
|
|
value: BottomNavigationBarType.shifting,
|
|
child: Text('Shifting'),
|
|
)
|
|
],
|
|
)
|
|
],
|
|
),
|
|
body: Center(child: _buildTransitionsStack()),
|
|
bottomNavigationBar: botNavBar,
|
|
);
|
|
}
|
|
}
|