- 本篇参考资料《Flutter实战》
- 本篇文章只是本人看书的理解和整理的笔记,更完整的内容还在书上!
- 电子书链接:https://book.flutterchina.club/
- Flutter中文社区链接:https://flutterchina.club/
- 尊重原作者,能支持购买实体书当然最好
本章讲解了ListView等各种可滚动控件的使用,本人根据本章内容完成了一个小demo
效果图如下:
效果图
完整代码仅286行:
import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
//滚动列表的学习
class MyScrollPage extends StatefulWidget {
@override
_MyScrollPageState createState() => _MyScrollPageState();
}
class _MyScrollPageState extends State<MyScrollPage> {
final pages = [MyListView(), MyGridView(), MyCustomScrollView()];
final List<BottomNavigationBarItem> bottomNavItems = [
BottomNavigationBarItem(
backgroundColor: Colors.blue,
icon: Icon(Icons.list),
title: Text("ListView"),
),
BottomNavigationBarItem(
backgroundColor: Colors.green,
icon: Icon(Icons.apps),
title: Text("GridView"),
),
BottomNavigationBarItem(
backgroundColor: Colors.amber,
icon: Icon(Icons.label_outline),
title: Text("CustomScroll"),
),
];
int _currentIndex;
@override
void initState() {
super.initState();
_currentIndex = 0;
}
@override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: bottomNavItems,
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
fixedColor: Colors.blue[300],
onTap: (index) {
_changePage(index);
},
),
body: pages[_currentIndex],
);
}
/*切换页面*/
void _changePage(int index) {
/*如果点击的导航项不是当前项 切换 */
if (index != _currentIndex) {
setState(() {
_currentIndex = index;
});
}
}
}
//tab 1
class MyListView extends StatefulWidget {
@override
_MyListViewState createState() => _MyListViewState();
}
class _MyListViewState extends State<MyListView> {
static const loadingTag = "##loading##"; //表尾标记
var _words = <String>[loadingTag];
ScrollController _controller = ScrollController();
bool showToTopBtn = false; //是否显示“返回到顶部”按钮
@override
void initState() {
super.initState();
_retrieveData();
_controller.addListener(() {
print(_controller.offset);
if (_controller.offset < 1000 && showToTopBtn) {
setState(() {
showToTopBtn = false;
});
} else if (_controller.offset >= 1000 && showToTopBtn == false) {
setState(() {
showToTopBtn = true;
});
}
});
}
@override
void dispose() {
//为了避免内存泄露,需要调用_controller.dispose
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("ListView"),
),
body: Column(
children: <Widget>[
ListTile(title: Text("商品列表")),
Divider(
color: Colors.blue,
),
Expanded(
child: _buildListView(),
),
],
),
floatingActionButton: !showToTopBtn
? null
: FloatingActionButton(
child: Icon(Icons.arrow_upward),
onPressed: () {
//返回到顶部时执行动画
_controller.animateTo(.0,
duration: Duration(milliseconds: 200), curve: Curves.ease);
},
),
);
}
void _retrieveData() {
Future.delayed(Duration(seconds: 2)).then((e) {
setState(() {
//重新构建列表
_words.insertAll(
_words.length - 1,
//每次生成20个单词
generateWordPairs().take(20).map((e) => e.asPascalCase).toList());
});
});
}
Widget _buildListView() {
return ListView.separated(
controller: _controller,
itemCount: _words.length,
separatorBuilder: (BuildContext context, int index) {
return index % 2 == 0
? Divider(
color: Colors.blue,
)
: Divider(
color: Colors.red,
);
},
itemBuilder: (BuildContext context, int index) {
if (_words[index] == loadingTag) {
//不足100条,继续获取数据
if (_words.length - 1 < 100) {
_retrieveData();
//加载时显示loading
return Container(
padding: const EdgeInsets.all(16.0),
alignment: Alignment.center,
child: SizedBox(
width: 24.0,
height: 24.0,
child: CircularProgressIndicator(strokeWidth: 2.0)),
);
} else {
//已经加载了100条数据,不再获取数据。
return Container(
alignment: Alignment.center,
padding: EdgeInsets.all(16.0),
child: Text(
"没有更多了",
style: TextStyle(color: Colors.grey[100]),
));
}
}
return ListTile(
title: Text(_words[index]),
);
});
}
}
//tab 2
class MyGridView extends StatefulWidget {
@override
_MyGridViewState createState() => _MyGridViewState();
}
class _MyGridViewState extends State<MyGridView> {
@override
Widget build(BuildContext context) {
return GridView.extent(
maxCrossAxisExtent: 300, //横轴三个子widget
childAspectRatio: 1.0, //宽高
children: <Widget>[
Icon(Icons.ac_unit),
Icon(Icons.airport_shuttle),
Icon(Icons.all_inclusive),
Icon(Icons.beach_access),
Icon(Icons.cake),
Icon(Icons.free_breakfast)
]);
}
}
//tab 3
class MyCustomScrollView extends StatefulWidget {
@override
_MyCustomScrollViewState createState() => _MyCustomScrollViewState();
}
class _MyCustomScrollViewState extends State<MyCustomScrollView> {
@override
Widget build(BuildContext context) {
//因为本路由没有使用Scaffold,为了让子级Widget(如Text)使用
//Material Design 默认的样式风格,我们使用Material作为本路由的根。
return Material(
child: CustomScrollView(
slivers: <Widget>[
//AppBar,包含一个导航栏
SliverAppBar(
pinned: true,
expandedHeight: 150.0,
flexibleSpace: FlexibleSpaceBar(
title: Text(
'Demo',
style: TextStyle(color: Colors.blue[100]),
),
background: Image.asset(
"assets/images/demo_pic1.jpeg",
fit: BoxFit.cover,
),
),
),
SliverPadding(
padding: const EdgeInsets.all(8.0),
sliver: new SliverGrid(
//Grid
gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, //Grid按两列显示
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0,
),
delegate: new SliverChildBuilderDelegate(
(BuildContext context, int index) {
//创建子widget
return new Container(
alignment: Alignment.center,
color: Colors.cyan[100 * (index % 9)],
child: new Text('grid item $index'),
);
},
childCount: 20,
),
),
),
//List
new SliverFixedExtentList(
itemExtent: 50.0,
delegate: new SliverChildBuilderDelegate(
(BuildContext context, int index) {
//创建列表项
return new Container(
alignment: Alignment.center,
color: Colors.lightBlue[100 * (index % 9)],
child: new Text('list item $index'),
);
}, childCount: 50 //50个列表项
),
),
],
),
);
}
}
网友评论