Skip to content

Instantly share code, notes, and snippets.

@327100395
Last active April 9, 2020 10:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 327100395/9dee2497a99e560f52c6ecd541242c7b to your computer and use it in GitHub Desktop.
Save 327100395/9dee2497a99e560f52c6ecd541242c7b to your computer and use it in GitHub Desktop.
仿微信app底部导航图标渐变
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(primaryColor: Colors.blue),
home: MyHomePage(title: 'test wechat msg'),
);
}
}
final StreamController<StreamModel> streamController =
StreamController.broadcast();
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final List<TabBarModel> pages = [
TabBarModel(
title: '微信',
icon: 'chat',
page: Page('微信'),
),
TabBarModel(
title: '通讯录',
icon: 'contacts',
page: Page('通讯录'),
),
TabBarModel(
title: '发现',
icon: 'discover',
page: Page('发现'),
),
TabBarModel(
title: '我',
icon: 'me',
page: Page('me'),
),
];
List bars = new List<BottomNavigationBarItem>();
int currentIndex = 0;
PageController pageController;
double lastPage = 0.0;
@override
void initState() {
// TODO: implement initState
super.initState();
for (int i = 0; i < pages.length; i++) {
TabBarModel model = pages[i];
bars.add(
BottomNavigationBarItem(
icon: BottomNavIcon(
model.title,
'assets/images/tabbar_' + model.icon + '_c.webp',
i,
streamController: streamController,
),
activeIcon: BottomNavIcon(
model.title,
'assets/images/tabbar_' + model.icon + '_s.webp',
i,
streamController: streamController,
isActive: true,
),
title: Center(),
),
);
}
pageController = PageController(initialPage: currentIndex);
pageController.addListener(() {
int currentPage = pageController.page.toInt();
//当前页面的page是double类型的, 把它减去当前页面的int类型就可以得出当前页面到下一个页面的偏移量了
double t = pageController.page - currentPage;
//根据上一次的页面位置获得方向
if (lastPage <= pageController.page) {
//向右滑动时currentPage是当前页
//从当前页过渡到下一页
streamController.sink.add(StreamModel(
timeline: t, index: currentPage, gotoIndex: currentPage + 1));
} else {
//向左滑动时currentPage是上一页
//从当前页过渡到上一页
streamController.sink.add(StreamModel(
timeline: t, index: currentPage + 1, gotoIndex: currentPage));
}
lastPage = pageController.page;
});
}
@override
Widget build(BuildContext context) {
final BottomNavigationBar bottomNavigationBar = new BottomNavigationBar(
items: bars,
type: BottomNavigationBarType.fixed,
currentIndex: currentIndex,
onTap: (int index) {
setState(() => currentIndex = index);
pageController.jumpToPage(currentIndex);
},
unselectedFontSize: 18.0,
selectedFontSize: 18.0,
);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: PageView.builder(
controller: pageController,
itemCount: pages.length,
itemBuilder: (BuildContext context, int index) => pages[index].page,
onPageChanged: (index) => setState(() => currentIndex = index),
),
bottomNavigationBar: new Container(
decoration: BoxDecoration(
border: Border(top: BorderSide(color: Colors.grey, width: 0.2))),
child: bottomNavigationBar,
),
);
}
}
class TabBarModel {
const TabBarModel({this.title, this.page, this.icon});
final String title;
final String icon;
final Widget page;
}
class Page extends StatelessWidget {
final String title;
Page(this.title);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white30,
child: Center(
child: Text(title),
),
);
}
}
class StreamModel {
const StreamModel({this.timeline, this.index, this.gotoIndex});
final double timeline;
final int index;
final int gotoIndex;
}
// ignore: must_be_immutable
class BottomNavIcon extends StatelessWidget {
final StreamController<StreamModel> streamController;
final int index;
final String img;
final String title;
final double fontSize;
Color _color;
Color _activeColor;
final bool isActive;
BottomNavIcon(this.title, this.img, this.index,
{@required this.streamController,
this.isActive = false,
this.fontSize = 18.0,
Color color = Colors.grey,
Color activeColor = Colors.blue}) {
_color = isActive ? activeColor : color;
_activeColor = isActive ? color : activeColor;
}
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: streamController.stream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
final StreamModel data = snapshot.data;
double t = 0.0;
if (data != null) {
//开始的index
if (data.index == index) {
t = data.index > data.gotoIndex
? data.timeline
: 1.0 - data.timeline;
print("this${data.index}:${t}");
}
//结束的index
if (data.gotoIndex == index) {
t = data.index > data.gotoIndex
? 1.0 - data.timeline //开始的index大于结束的index方向向左
: data.timeline; //小于方向向右
//过渡到的图标颜色的插值超过0.6时, 个人感觉当前颜色和结束的哪个颜色相差太多,
//所以超过0.6时恢复默认颜色
t = t >= 0.6 ? 1 : t;
print("goto${data.gotoIndex}:${t}");
}
}
if (t > 0.0 && t < 1.0) {
//color.lerp 获取两种颜色之间的线性插值
return Column(
children: <Widget>[
ImageIcon(AssetImage(this.img),
color: Color.lerp(_color, _activeColor, t)),
Text(title,
style: TextStyle(
fontSize: fontSize,
color: Color.lerp(_color, _activeColor, t))),
],
);
}
return Column(
children: <Widget>[
ImageIcon(AssetImage(this.img),
color:
Color.fromRGBO(_color.red, _color.green, _color.blue, 1)),
Text(title,
style: TextStyle(
fontSize: fontSize,
color: Color.fromRGBO(
_color.red, _color.green, _color.blue, 1))),
],
);
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment