35.监听滚动事件
class ScrollControllerTestRoute extends StatefulWidget {
@override
ScrollControllerTestRouteState createState() {
return new ScrollControllerTestRouteState();
}
}
class ScrollControllerTestRouteState extends State<ScrollControllerTestRoute> {
ScrollController _controller = new ScrollController();
bool showToTopBtn = false; //是否显示“返回到顶部”按钮
@override
void initState() {
super.initState();
//监听滚动事件,打印滚动位置
_controller.addListener(() {
print(_controller.offset); //打印滚动位置
//当偏移量小于1000像素,不显示按钮
//这里后面之所以&&showToTopBtn,是因为可以通过判断它是true或false省得不断地调用setState,影响性能
if (_controller.offset < 1000 && showToTopBtn) {
setState(() {
showToTopBtn = false;
});
//当偏移量大于等于1000像素,显示按钮
//&&showToTopBtn的理由同上
} 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("滚动控制")),
body: Scrollbar(//滚动条
child: ListView.builder(
itemCount: 100,//滚动的总条数
itemExtent: 50.0, //列表项高度固定时,显式指定高度是一个好习惯(性能消耗小)
controller: _controller,//添加已加入监听功能的控制器
itemBuilder: (context, index) {
return ListTile(title: Text("$index"),);//显示文本
}
),
),
//通过判断showToTopBtn的值来显示按钮
floatingActionButton: !showToTopBtn ? null : FloatingActionButton(
child: Icon(Icons.arrow_upward),
onPressed: () {
//执行动画返回到顶部
_controller.animateTo(.0,
duration: Duration(milliseconds: 200),
curve: Curves.ease
);
}
),
);
}
}
效果:
36.根组件数据共享给子组件
class InheritedWidgetTestRoute extends StatefulWidget {
@override
_InheritedWidgetTestRouteState createState() => new _InheritedWidgetTestRouteState();
}
class _InheritedWidgetTestRouteState extends State<InheritedWidgetTestRoute> {
//state的数据
int count = 0;
@override
Widget build(BuildContext context) {
return Center(
//根组件,用于共享数据给它的子组件
child: ShareDataWidget( //使用ShareDataWidget
//构建根组件要用到的参数data
data: count,
//根组件的子组件
child: Column(
//垂直水平方向居中
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(bottom: 20.0),
//根组件的子组件的子组件...
child: _TestWidget(),//子widget中依赖ShareDataWidget
),
RaisedButton(
child: Text("Increment"),
//每点击一次,将count自增,然后重新build,ShareDataWidget的data将被更新
onPressed: () => setState(() => ++count),
)
],
),
),
);
}
}
class _TestWidget extends StatefulWidget {
@override
__TestWidgetState createState() => new __TestWidgetState();
}
class __TestWidgetState extends State<_TestWidget> {
@override
Widget build(BuildContext context) {
//使用ShareDataWidget中的共享数据
return Text(ShareDataWidget
.of(context)
.data
.toString());
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
//父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
//如果build中没有依赖InheritedWidget,则此回调不会被调用。
print("Dependencies change");
}
}
class ShareDataWidget extends InheritedWidget {
ShareDataWidget({
//接收_InheritedWidgetTestRouteState组件build时传入的两个参数
@required this.data,
Widget child
}) :super(child: child);
final int data; //需要在子树中共享的数据,保存点击次数
//定义一个便捷方法,方便子树中的widget获取共享数据
static ShareDataWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
}
//该回调决定当data发生变化时,是否通知子树中依赖data的Widget
@override
bool updateShouldNotify(ShareDataWidget old) {
//如果返回true,则子树中依赖(build函数中有调用)本widget
//的子widget的`state.didChangeDependencies`会被调用
return old.data != data;
}
}
效果:
37.改变主题
class ThemeTestRoute extends StatefulWidget {
@override
_ThemeTestRouteState createState() => new _ThemeTestRouteState();
}
class _ThemeTestRouteState extends State<ThemeTestRoute> {
Color _themeColor = Colors.teal; //当前路由主题色
@override
Widget build(BuildContext context) {
ThemeData themeData = Theme.of(context);
return Theme(
data: ThemeData(
primarySwatch: _themeColor, //用于导航栏、FloatingActionButton的背景色等
iconTheme: IconThemeData(color: _themeColor) //用于Icon颜色
),
child: Scaffold(
appBar: AppBar(title: Text("主题测试")),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
//第一行Icon使用主题中的iconTheme
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.favorite),
Icon(Icons.airport_shuttle),
Text(" 颜色跟随主题")
]
),
//为第二行Icon自定义颜色(固定为黑色)
Theme(
//局部主题覆盖全局主题
data: themeData.copyWith(
iconTheme: themeData.iconTheme.copyWith(
color: Colors.black
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.favorite),
Icon(Icons.airport_shuttle),
Text(" 颜色固定黑色")
]
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () => //切换主题
setState(() =>
_themeColor =
_themeColor == Colors.teal ? Colors.blue : Colors.teal
),
child: Icon(Icons.palette)
),
),
);
}
}
效果:
38.异步UI更新
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('标题'),
),
body: StreamBuilder<int>(
stream: counter(), //调用异步耗时任务
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {//snapshot会包含当前异步任务的状态信息及结果信息
//通过snapshot.hasError判断异步任务是否有错误
if (snapshot.hasError)
//打印错误
return Text('Error: ${snapshot.error}');
//onnectionState是一个枚举类,根据当前异步任务状态ConnectionState来返回不同的widget
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('没有Stream');
case ConnectionState.waiting:
return Text('等待数据...');
case ConnectionState.active:
return Text('active: ${snapshot.data}');
case ConnectionState.done:
return Text('Stream已关闭');
}
return null; // unreachable
},
),
);
}
}
//使用Stream来实现异步每隔一秒生成一个数字,每次返回一个int类型
Stream<int> counter() {
return Stream.periodic(Duration(seconds: 1), (i) {
return i;
});
}
效果: