项目案例 -- StatelessWidget
- 案例代码如下:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage(),
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("商品列表"),
),
body: SFContentBody(),
);
}
}
class SFContentBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(
children: [
SFProductItem("Apple1","MacBook1","https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fdl.bbs.9game.cn%2Fattachments%2Fforum%2F201507%2F29%2F154250no2g2zqiuiqvaiku.jpg&refer=http%3A%2F%2Fdl.bbs.9game.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1637072364&t=07974f838e1b49ecdecaba22f4af4fa2"),
SFProductItem("Apple2","MacBook2","https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fdl.bbs.9game.cn%2Fattachments%2Fforum%2F201507%2F29%2F154250no2g2zqiuiqvaiku.jpg&refer=http%3A%2F%2Fdl.bbs.9game.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1637072364&t=07974f838e1b49ecdecaba22f4af4fa2"),
SFProductItem("Apple3","MacBook3","https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fdl.bbs.9game.cn%2Fattachments%2Fforum%2F201507%2F29%2F154250no2g2zqiuiqvaiku.jpg&refer=http%3A%2F%2Fdl.bbs.9game.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1637072364&t=07974f838e1b49ecdecaba22f4af4fa2"),
],
);
}
}
class SFProductItem extends StatelessWidget {
final String title;
final String desc;
final String imageUrl;
final titleStyle = TextStyle(fontSize: 25,color: Colors.orange);
final descStyle = TextStyle(fontSize: 20,color: Colors.green);
//自定义构造函数
SFProductItem(this.title,this.desc,this.imageUrl);
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(8), //设置内边距
decoration: BoxDecoration(
border: Border.all(
width: 5, //设置边框的宽度
color: Colors.purple //设置边框的额颜色
)
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title,style: titleStyle),
SizedBox(height: 8),//设置间距
Text(desc,style: descStyle),
SizedBox(height: 8),
Image.network(imageUrl)
],
),
);
}
}
- 效果如下所示:
- 给widget添加Container的快捷键
Alt + Enter
- widget之间
设置间距
使用SizedBox(height: 8)
项目案例 -- StatefulWidget
-
StatefulWidget
最大的特点是:StatefulWidget
通过创建状态类_SFHomeContentState
,来管理自己的状态数据; - 案例代码如下:
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage()
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("商品列表")
),
body: SFHomeContent("上面是一个简单的计数器")
);
}
}
class SFHomeContent extends StatefulWidget {
final String message;
SFHomeContent(this.message);
@override
State createState() {
return _SFHomeContentState();
}
}
//
class _SFHomeContentState extends State{
var _counter = 0;
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_getButtons(),
Text("当前计数: $_counter",style: TextStyle(fontSize: 20)),
Text("${widget.message}",style: TextStyle(fontSize: 18))
],
),
);
}
Widget _getButtons(){
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RaisedButton(
child: Text("+",style: TextStyle(fontSize: 20,color: Colors.white)),
color: Colors.pink,
onPressed: (){
print("点击+");
setState(() {
_counter++;
});
},
),
RaisedButton(
child: Text("-",style: TextStyle(fontSize: 20,color: Colors.white)),
color: Colors.purple,
onPressed: (){
print("点击-");
setState(() {
_counter--;
});
},
)
],
);
}
}
-
class _SFHomeContentState extends State
,Widget _getButtons()
类名与方法名之前加下划线
表明属于私有的; -
class _SFHomeContentState extends State
:_SFHomeContentState
状态用来管理SFHomeContent
这个widget的状态数据的,即_SFHomeContentState
会绑定SFHomeContent
,_SFHomeContentState
中能通过widget属性
访问SFHomeContent
中的内容; - 代码运行效果:
StatefulWidget生命周期
- 所谓生命周期是指:目标组件从创建到销毁的整个过程,监听组件的生命周期以便在不同的时期执行不同的逻辑;
- Flutter组件的生命周期:
-
StatelessWidget
可以由父widget直接传入值,调用build
方法来创建,整个过程非常简单,其生命周期,主要关注构造函数
与build
方法; -
StatefulWidget
需要通过State
来管理其状态数据,并且监听状态的改变重新build整个widget;
-
-
StatefulWidget
生命周期的过程如下图所示:
- 1.执行
StatefulWidget
的构造函数; - 2.执行
StatefulWidget
的createState
方法,创建一个维护StatefulWidget
的State
对象; - 3.执行
State
的构造方法; - 4.执行
initState
方法,我们通常在此方法中执行一些数据初始化
操作,或者发送网络请求
; - 5.
didChangeDependencies
方法,在下面两种情况下会调用:- 调用
initState
时会调用; - 从其他对象中依赖一些数据发生改变时,比如InheritedWidget;
- 调用
- 6.执行
build
方法,渲染widget树; - 7.当 当前的widget不再使用时,会调用
dispose
方法进行销毁; - 8.手动调用
setState
方法,会根据最新的状态数据来重新调用build
方法,构建对应的widget; - 9.执行
didUpdateWidget
方法是在父widget触发重建rebuild时,系统会调用didUpdateWidget
方法; - 代码案例验证:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage()
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("商品列表")
),
body: SFHomeContent("上面是一个简单的计数器")
);
}
}
class SFHomeContent extends StatefulWidget {
final String message;
SFHomeContent(this.message){
print("SFHomeContent 构造方法");
}
@override
State createState() {
print("createState");
return _SFHomeContentState();
}
}
class _SFHomeContentState extends State{
var _counter = 0;
_SFHomeContentState(){
print("_SFHomeContentState 构造方法");
}
@override
void initState() {
super.initState();
print("_SFHomeContentState initState");
}
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RaisedButton(
child: Text("+",style: TextStyle(fontSize: 25,color: Colors.white)),
color: Colors.pinkAccent,
onPressed: (){
setState(() {
_counter++;
});
},
),
Text("${widget.message}",style: TextStyle(fontSize: 18))
],
),
);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("_SFHomeContentState didChangeDependencies");
}
@override
void didUpdateWidget(SFHomeContent oldWidget) {
super.didUpdateWidget(oldWidget);
print("_SFHomeContentState didUpdateWidget");
}
-
@override
void dispose() {
super.dispose();
print("_SFHomeContentState dispose");
}
}
- 执行结果如下:
-
StatefulWidget
生命周期复杂版的过程如下图所示:
mounded
时State内部设置的一个属性,不需要我们手动进行修改的,其主要作用是记录widget
对应的element
是否为空;dirty state
含义是脏的state,它实际是来标记Element
的,标记为dirty的Element会等待下一次的重绘检查,强制调用build方法
来构建我们的widget;clean state
含义是干净的state,它表示当前build出来的widget,下一次重绘检查不需要重新build;
Flutter的编程范式
- 命令式编程:就是一步步给计算机命令,告诉它我们想做什么事情;
- 声明式编程:通常是描述目标的性质,依赖哪些状态,并且当依赖的状态发生改变时,我们通过某些方式通知目标做出响应,声明式编程是依赖框架的;
- Flutter采用的是声明式编程;
- 命令式编程的代码案例:
final text = new Text();
var title = "Hello World";
text.setContent(title); //主动设置title
- 声明式编程的代码案例:
var title = "Hello World";
Text(title); //告诉Text内部显示的是title
基础Widget
文本Widget
- 在Flutter中使用
Text
组件来显示文本;
普通文本
-
控制文本布局的参数
:在构造函数中;-
textAlign
:文本的对齐方式; -
textDirection
:文本的排版方向; -
maxLines
:最大显示行数; -
overflow
:文本的截断规则;
-
-
控制文本样式的参数
:在构造函数的参数style
中;-
fontSize
:文本大小; -
color
:文本颜色; -
fontFamily
:设置字体; -
shaow
:文本阴影;
-
- 案例代码:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage()
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("基础widget")
),
body: SFHomeContent()
);
}
}
class SFHomeContent extends StatefulWidget {
@override
State createState() {
print("createState");
return _SFHomeContentState();
}
}
class _SFHomeContentState extends State{
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
return TextDemo();
}
}
class TextDemo extends StatelessWidget {
const TextDemo({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Text(
"基础widget \ndasd has发哈就困了打算看是的撒 \n但是开发双卡双待",
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 30,
color: Colors.red,
fontWeight: FontWeight.bold,
fontFamily: 'Courier'
),
);
}
}
富文本
- 富文本使用
Text.rich
,其中必选参数传入InlineSpan
,是一个抽象类,我们需要传入其子类即可,其子类有:-
TextSpan
:显示文本的; -
WidgetSpan
:显示图片的; placeholderSpace
-
- 案例代码如下:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage()
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("基础widget")
),
body: SFHomeContent()
);
}
}
class SFHomeContent extends StatefulWidget {
@override
State createState() {
print("createState");
return _SFHomeContentState();
}
}
class _SFHomeContentState extends State{
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
return TextRichDemo();
}
}
class TextRichDemo extends StatelessWidget {
const TextRichDemo({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Text.rich(
TextSpan(
children: [
TextSpan(text: "Hello World!!", style: TextStyle(fontSize: 20,color: Colors.green)),
TextSpan(text: "Hello iOS!!", style: TextStyle(fontSize: 20,color: Colors.red)),
WidgetSpan(child: Icon(Icons.favorite,color: Colors.red)),
TextSpan(text: "Hello Flutter!!", style: TextStyle(fontSize: 25,color: Colors.orange))
]
)
);
}
}
按钮Widget
- 常见的按钮类型有:
- RaisedButton
- FlatButton
- OutlineButton
- FloatingActionButton
RaisedButton
-
RaisedButton
是带有一定圆角和阴影以及灰色背景的按钮,并且在点击的时候有动画效果,其构造函数如下:
class RaisedButton extends MaterialButton {
/// Create a filled button.
///
/// The [autofocus] and [clipBehavior] arguments must not be null.
/// Additionally, [elevation], [hoverElevation], [focusElevation],
/// [highlightElevation], and [disabledElevation] must be non-negative, if
/// specified.
const RaisedButton({
Key key,
@required VoidCallback onPressed,
VoidCallback onLongPress,
ValueChanged onHighlightChanged,
MouseCursor mouseCursor,
ButtonTextTheme textTheme,
Color textColor,
Color disabledTextColor,
Color color,
Color disabledColor,
Color focusColor,
Color hoverColor,
Color highlightColor,
Color splashColor,
Brightness colorBrightness,
double elevation,
double focusElevation,
double hoverElevation,
double highlightElevation,
double disabledElevation,
EdgeInsetsGeometry padding,
VisualDensity visualDensity,
ShapeBorder shape,
Clip clipBehavior = Clip.none,
FocusNode focusNode,
bool autofocus = false,
MaterialTapTargetSize materialTapTargetSize,
Duration animationDuration,
Widget child,
})
FlatButton
-
FlatButton
:默认没有背景颜色,不带阴影,高亮状态下有背景,构造函数如下:
class FlatButton extends MaterialButton {
/// Create a simple text button.
///
/// The [autofocus] and [clipBehavior] arguments must not be null.
const FlatButton({
Key key,
@required VoidCallback onPressed,
VoidCallback onLongPress,
ValueChanged onHighlightChanged,
MouseCursor mouseCursor,
ButtonTextTheme textTheme,
Color textColor,
Color disabledTextColor,
Color color,
Color disabledColor,
Color focusColor,
Color hoverColor,
Color highlightColor,
Color splashColor,
Brightness colorBrightness,
EdgeInsetsGeometry padding,
VisualDensity visualDensity,
ShapeBorder shape,
Clip clipBehavior = Clip.none,
FocusNode focusNode,
bool autofocus = false,
MaterialTapTargetSize materialTapTargetSize,
@required Widget child,
})
OutlineButton
-
OutlineButton
:带有边框的按钮,构造函数如下:
class OutlineButton extends MaterialButton {
/// Create an outline button.
///
/// The [highlightElevation] argument must be null or a positive value
/// and the [autofocus] and [clipBehavior] arguments must not be null.
const OutlineButton({
Key key,
@required VoidCallback onPressed,
VoidCallback onLongPress,
MouseCursor mouseCursor,
ButtonTextTheme textTheme,
Color textColor,
Color disabledTextColor,
Color color,
Color focusColor,
Color hoverColor,
Color highlightColor,
Color splashColor,
double highlightElevation,
this.borderSide,
this.disabledBorderColor,
this.highlightedBorderColor,
EdgeInsetsGeometry padding,
VisualDensity visualDensity,
ShapeBorder shape,
Clip clipBehavior = Clip.none,
FocusNode focusNode,
bool autofocus = false,
Widget child,
})
FloatingActionButton
-
FloatingActionButton
:悬浮按钮,构造函数如下:
class FloatingActionButton extends StatelessWidget {
/// Creates a circular floating action button.
///
/// The [mini] and [clipBehavior] arguments must not be null. Additionally,
/// [elevation], [highlightElevation], and [disabledElevation] (if specified)
/// must be non-negative.
const FloatingActionButton({
Key key,
this.child,
this.tooltip,
this.foregroundColor,
this.backgroundColor,
this.focusColor,
this.hoverColor,
this.splashColor,
this.heroTag = const _DefaultHeroTag(),
this.elevation,
this.focusElevation,
this.hoverElevation,
this.highlightElevation,
this.disabledElevation,
@required this.onPressed,
this.mouseCursor,
this.mini = false,
this.shape,
this.clipBehavior = Clip.none,
this.focusNode,
this.autofocus = false,
this.materialTapTargetSize,
this.isExtended = false,
})
- 案例代码:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage()
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("基础widget")
),
body: SFHomeContent()
);
}
}
class SFHomeContent extends StatefulWidget {
@override
State createState() {
print("createState");
return _SFHomeContentState();
}
}
class _SFHomeContentState extends State{
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
return ButtonWidget();
}
}
class ButtonWidget extends StatelessWidget {
const ButtonWidget({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: [
RaisedButton(
child: Text("RaisedButton"),
textColor: Colors.white,
onPressed: (){
print("RaisedButton click");
},
),
FlatButton(
child: Text("FlatButton"),
onPressed: (){
print("FlatButton click");
},
),
OutlineButton(
child: Text("OutlineButton"),
onPressed: (){
print("OutlineButton click");
},
),
FloatingActionButton(
child: Icon(Icons.add),
onPressed: (){
print("FloatingActionButton click");
},
),
//自定义button
FlatButton(
color: Colors.green,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.favorite,color: Colors.red),
Text("喜欢作者")
],
),
onPressed: (){
print("FlatButton");
},
)
],
);
}
}
- 执行结果如下图所示:
-
ShapeBorder
:设置边框,是一个抽象类,其常用子类为RoundedRectangleBorder
- 1.默认情况下Button上下会有一定的间距,可通过
materialTapTargetSize
属性,去除间距; - 2.Button在没有文本的情况下,会显示默认尺寸大小,可通过
ButtonTheme
属性,自定义任意尺寸大小; - 3.可通过
padding
属性,去除内容边距; - 案例代码:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage()
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("基础widget")
),
body: SFHomeContent()
);
}
}
class SFHomeContent extends StatefulWidget {
@override
State createState() {
print("createState");
return _SFHomeContentState();
}
}
//`ButtonTheme`属性,自定义任意尺寸大小
//通过`padding`属性,去除内容边距
class _SFHomeContentState extends State{
final imageUrl = "";
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
return Column(
children: [
ButtonTheme(
minWidth: 30,
height: 15,
child: FlatButton(
color: Colors.red,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
child: Text("FlatButton"),
textColor: Colors.white,
padding: EdgeInsets.all(0),
onPressed: (){
print("click FlatButton");
},
),
)
],
);
}
}
class ButtonExtra01 extends StatelessWidget {
const ButtonExtra01({
Key key,
}) : super(key: key);
//`materialTapTargetSize`属性,去除间距
@override
Widget build(BuildContext context) {
return Column(
children: [
FlatButton(
color: Colors.red,
child: Text("FlatButton"),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
textColor: Colors.white,
onPressed: (){
print("click FlatButton");
},
),
FlatButton(
color: Colors.red,
child: Text("FlatButton"),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
textColor: Colors.white,
onPressed: (){
print("click FlatButton");
},
)
],
);
}
}
图片Widget
- Image:图片组件,其构造函数如下:
class Image extends StatefulWidget {
/// Creates a widget that displays an image.
///
/// To show an image from the network or from an asset bundle, consider using
/// [new Image.network] and [new Image.asset] respectively.
///
/// The [image], [alignment], [repeat], and [matchTextDirection] arguments
/// must not be null.
///
/// Either the [width] and [height] arguments should be specified, or the
/// widget should be placed in a context that sets tight layout constraints.
/// Otherwise, the image dimensions will change as the image is loaded, which
/// will result in ugly layout changes.
///
/// Use [filterQuality] to change the quality when scaling an image.
/// Use the [FilterQuality.low] quality setting to scale the image,
/// which corresponds to bilinear interpolation, rather than the default
/// [FilterQuality.none] which corresponds to nearest-neighbor.
///
/// If [excludeFromSemantics] is true, then [semanticLabel] will be ignored.
const Image({
Key key,
@required this.image,
this.frameBuilder,
this.loadingBuilder,
this.errorBuilder,
this.semanticLabel,
this.excludeFromSemantics = false,
this.width,
this.height,
this.color,
this.colorBlendMode,
this.fit,
this.alignment = Alignment.center,
this.repeat = ImageRepeat.noRepeat,
this.centerSlice,
this.matchTextDirection = false,
this.gaplessPlayback = false,
this.isAntiAlias = false,
this.filterQuality = FilterQuality.low,
})
-
@required this.image
:必选参数,参数类型为ImageProvider
是一个抽象类,常见子类有:NetworkImage
与AssetImage
NetworkImage加载网络图片
- 案例代码如下:
class NetWorkImage extends StatelessWidget {
const NetWorkImage({
Key key,
@required this.imageUrl,
}) : super(key: key);
final String imageUrl;
@override
Widget build(BuildContext context) {
return Image(
image: NetworkImage(imageUrl),
width: 200,
height: 200,
fit: BoxFit.fill,
alignment: Alignment.bottomLeft,
repeat: ImageRepeat.repeatY,
);
}
}
-
fit
:图片的显示模式,等价于OC中的contentMode
-
alignment
:图片的对齐方式; -
repeat
:当图片未填充满控件时,可设置重复填充;
AssetImage加载本地图片
- 首先在Flutter项目中引入图片资源;
- 然后在
pub spec.yaml
文件中进行配置,然后执行flutter pub get
命令; - 最后使用图片;
- 流程如下:
- 代码案例:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage()
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("基础widget")
),
body: SFHomeContent()
);
}
}
class SFHomeContent extends StatefulWidget {
@override
State createState() {
print("createState");
return _SFHomeContentState();
}
}
class _SFHomeContentState extends State{
final imageUrl = "";
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
return Image(
image: AssetImage("asset/images/180.png"),
);
}
}
- 案例代码:
class ImageExtra extends StatelessWidget {
const ImageExtra({
Key key,
@required this.imageUrl,
}) : super(key: key);
final String imageUrl;
@override
Widget build(BuildContext context) {
return FadeInImage(
placeholder: AssetImage("asset/images/180.png"), //设置占位图
image: NetworkImage(imageUrl),//设置网络图片
fadeOutDuration: Duration(milliseconds: 1),//淡入淡出的动画效果
fadeInDuration: Duration(milliseconds: 1),
);
}
}
-
placeholder
属性可设置占位图片;
Icon图标
- 1.Icon可设置字体图片与图片图标;
- 2.字体图片与图片图标是矢量图,放大不会失真;
- 3.图标可设置颜色;
- 案例代码:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage()
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("基础widget")
),
body: SFHomeContent()
);
}
}
class SFHomeContent extends StatefulWidget {
@override
State createState() {
print("createState");
return _SFHomeContentState();
}
}
class _SFHomeContentState extends State{
final imageUrl = "";
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
//1.Icon可设置字体图片与图片图标
//2.字体图片与图片图标是矢量图,放大不会失真
//3.图标可设置颜色
//4.下面三种写法等价
return Icon(Icons.pets,size: 200,color: Colors.red);
// return Icon(IconData(0xe91d, fontFamily: 'MaterialIcons'),size: 200,color: Colors.red);
// return Text("\ue91d",style: TextStyle(fontSize: 100,fontFamily: 'MaterialIcons',color: Colors.red));
}
}
输入框Widget -- TextField
- TextField的构造函数:
class TextField extends StatefulWidget {
/// Creates a Material Design text field.
///
/// If [decoration] is non-null (which is the default), the text field requires
/// one of its ancestors to be a [Material] widget.
///
/// To remove the decoration entirely (including the extra padding introduced
/// by the decoration to save space for the labels), set the [decoration] to
/// null.
///
/// The [maxLines] property can be set to null to remove the restriction on
/// the number of lines. By default, it is one, meaning this is a single-line
/// text field. [maxLines] must not be zero.
///
/// The [maxLength] property is set to null by default, which means the
/// number of characters allowed in the text field is not restricted. If
/// [maxLength] is set a character counter will be displayed below the
/// field showing how many characters have been entered. If the value is
/// set to a positive integer it will also display the maximum allowed
/// number of characters to be entered. If the value is set to
/// [TextField.noMaxLength] then only the current length is displayed.
///
/// After [maxLength] characters have been input, additional input
/// is ignored, unless [maxLengthEnforced] is set to false. The text field
/// enforces the length with a [LengthLimitingTextInputFormatter], which is
/// evaluated after the supplied [inputFormatters], if any. The [maxLength]
/// value must be either null or greater than zero.
///
/// If [maxLengthEnforced] is set to false, then more than [maxLength]
/// characters may be entered, and the error counter and divider will
/// switch to the [decoration.errorStyle] when the limit is exceeded.
///
/// The text cursor is not shown if [showCursor] is false or if [showCursor]
/// is null (the default) and [readOnly] is true.
///
/// The [selectionHeightStyle] and [selectionWidthStyle] properties allow
/// changing the shape of the selection highlighting. These properties default
/// to [ui.BoxHeightStyle.tight] and [ui.BoxWidthStyle.tight] respectively and
/// must not be null.
///
/// The [textAlign], [autofocus], [obscureText], [readOnly], [autocorrect],
/// [maxLengthEnforced], [scrollPadding], [maxLines], [maxLength],
/// [selectionHeightStyle], [selectionWidthStyle], and [enableSuggestions]
/// arguments must not be null.
///
/// See also:
///
/// * [maxLength], which discusses the precise meaning of "number of
/// characters" and how it may differ from the intuitive meaning.
const TextField({
Key key,
this.controller,
this.focusNode,
this.decoration = const InputDecoration(),
TextInputType keyboardType,
this.textInputAction,
this.textCapitalization = TextCapitalization.none,
this.style,
this.strutStyle,
this.textAlign = TextAlign.start,
this.textAlignVertical,
this.textDirection,
this.readOnly = false,
ToolbarOptions toolbarOptions,
this.showCursor,
this.autofocus = false,
this.obscuringCharacter = '•',
this.obscureText = false,
this.autocorrect = true,
SmartDashesType smartDashesType,
SmartQuotesType smartQuotesType,
this.enableSuggestions = true,
this.maxLines = 1,
this.minLines,
this.expands = false,
this.maxLength,
this.maxLengthEnforced = true,
this.onChanged,
this.onEditingComplete,
this.onSubmitted,
this.inputFormatters,
this.enabled,
this.cursorWidth = 2.0,
this.cursorRadius,
this.cursorColor,
this.selectionHeightStyle = ui.BoxHeightStyle.tight,
this.selectionWidthStyle = ui.BoxWidthStyle.tight,
this.keyboardAppearance,
this.scrollPadding = const EdgeInsets.all(20.0),
this.dragStartBehavior = DragStartBehavior.start,
this.enableInteractiveSelection = true,
this.onTap,
this.mouseCursor,
this.buildCounter,
this.scrollController,
this.scrollPhysics,
this.autofillHints,
})
- 案例代码如下:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage()
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("基础widget")
),
body: SFHomeContent()
);
}
}
class SFHomeContent extends StatefulWidget {
@override
State createState() {
print("createState");
return _SFHomeContentState();
}
}
class _SFHomeContentState extends State{
final usernameTextEditController = TextEditingController();
final passwordTextEditController = TextEditingController();
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
return Theme(
data: ThemeData(
primaryColor: Colors.red
),
child: Padding(
padding: EdgeInsets.all(8.0),
child:Column(
children: [
TextField(
controller: usernameTextEditController,
decoration: InputDecoration(
labelText: "username",
icon: Icon(Icons.people),
hintText: "请输入用户名",
border: OutlineInputBorder(),
filled: true,
fillColor: Colors.red[100],
),
onChanged: (value){
print("onChanged:$value");
},
onSubmitted: (value){
print("onSubmitted:$value");
},
),
SizedBox(height: 10),
TextField(
controller: passwordTextEditController,
decoration: InputDecoration(
labelText: "password",
icon: Icon(Icons.lock),
hintText: "请输入密码",
border: InputBorder.none,
filled: true,
fillColor: Colors.red[100],
),
onChanged: (value){
print("onChanged:$value");
},
onSubmitted: (value){
print("onSubmitted:$value");
},
),
SizedBox(height: 20),
Container(
width: 300,
height: 35,
child: FlatButton(
child: Text("登 录",style: TextStyle(fontSize: 20,color: Colors.white)),
color: Colors.blue,
onPressed: (){
print("login");
final username = usernameTextEditController.text;
final password = passwordTextEditController.text;
print("username = $username,password = $password");
},
),
)
],
)
)
);
}
}
- 运行结果如下:
-
Theme
:设置整个UI的主题风格; - 给目标组件
设置宽高
,通常是在目标组件外面再包装一层Container
,然后设置width
与height
属性; -
onChanged
:监听输入框文本变化的回调; -
onSubmitted
:监听输入框提交时的回调; - 在登录按钮的点击回调中,获取输入框的文本是通过给输入框绑定一个
TextEditingController
,然后通过TextEditingController.text
获取输入框的文本;
单子布局组件
- 常见的单子布局组件有:Align,Center,Padding,Container
Align组件
- Align:即对齐;
- Center组件:是继承自Align组件的,本质上Align与Center等价;
- 案例代码如下:
class _SFHomeContentState extends State{
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
return Container(
color: Colors.red,
child: Align(
child: Icon(Icons.pets,size: 50),
alignment: Alignment(0,0),
widthFactor: 5,
heightFactor: 3,
),
);
}
}
- Align默认占据整个屏幕大小;
- widthFactor与heightFactor 是child的尺寸的倍数大小 作为Align的尺寸大小;
Padding组件
- Padding:主要用来设置子Widget到父Widget的边距;
- 其构造函数:
const Padding({
Key key,
@required this.padding,
Widget child,
})
- 案例代码:
class _SFHomeContentState extends State{
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
return Column(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 5),
child: Text("日你啊满满的个单",style: TextStyle(fontSize: 20,backgroundColor: Colors.red)),
),
Padding(
padding: const EdgeInsets.only(bottom: 5),
child: Text("日你啊满满的个单",style: TextStyle(fontSize: 20,backgroundColor: Colors.red)),
),
Text("日你啊满满的个单",style: TextStyle(fontSize: 20,backgroundColor: Colors.red))
],
);
}
}
Container组件
- Container:顾名思义为容器组件,
- 其构造函数如下:
Container({
Key key,
this.alignment,
this.padding,
this.color,
this.decoration,
this.foregroundDecoration,
double width,
double height,
BoxConstraints constraints,
this.margin,
this.transform,
this.child,
this.clipBehavior = Clip.none,
})
- 案例代码:
class _SFHomeContentState extends State{
@override
Widget build(BuildContext context) {
return Container(
// color: Colors.red,
width: 200,
height: 200,
child: Text("这是一个文本",style: TextStyle(color: Colors.white),),
alignment: Alignment(0,0),
padding: EdgeInsets.all(20),
margin: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.red,
border: Border.all(
width: 5,
color: Colors.purple
),
borderRadius: BorderRadius.circular(100),
boxShadow: [
BoxShadow(color: Colors.orange,offset: Offset(10,10),spreadRadius: 5,blurRadius: 10),
BoxShadow(color: Colors.blue,offset: Offset(-10,10),spreadRadius: 5,blurRadius: 10)
]
),
);
}
}
-
padding
:设置容器内间距; -
margin
:设置容器外间距; -
decoration
:设置装饰,内部包含边框,圆角,阴影等等; - 效果图:
多子布局组件
- 常见的多子布局组件有:Flex,Row,Column
Flex组件
- Row组件与Column组件都是继承自Flex组件;
- 主轴:mainAxis即主方向的轴;
- 交叉轴:crosssAxis
Row组件
Row的主轴方向为:水平方向,交叉轴为竖直方向;
-
Row在主轴方向即
水平方向
上占据比较大的空间
;- 若在水平方向上希望包裹内容,那么设置
mainAxisSize = min
;
- 若在水平方向上希望包裹内容,那么设置
Row在交叉轴方向即
竖直方向
上包裹内容
;-
mainAxisAlignment
:主轴方向上的对齐方式-
start
:主轴的开始位置依次摆放元素; -
end
:主轴的结束位置依次摆放元素; -
center
:主轴中心点对齐; -
spaceBetween
:左右间距为0,其他元素之间间距平分; -
spaceAround
:左右间距是其他元素之间间距的一半; -
spaceEvenly
:所有间距平分;
-
-
crossAxisAlignment
:交叉方向上的对齐方式-
start
:交叉轴的开始位置依次摆放元素; -
end
:交叉轴的结束位置依次摆放元素; -
center
:交叉轴中心点对齐; -
textBaseline
:基线对齐(必须有文本内容才有效果); -
stretch
:将所有子元素,拉伸到最大;
-
案例代码:mainAxisSize = min
class _SFHomeContentState extends State{
@override
Widget build(BuildContext context) {
return RaisedButton(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.bug_report),
Text("bug")
],
),
);
}
}
- 案例代码:
class RowDemo extends StatelessWidget {
const RowDemo({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
textBaseline: TextBaseline.alphabetic,
children: [
Container(width: 80,height: 60,color: Colors.red),
Container(width: 80,height: 80,color: Colors.green),
Container(width: 80,height: 100,color: Colors.blue),
Container(width: 80,height: 120,color: Colors.orange),
],
);
}
}
Flexible与Expanded
-
Flexible
:空间拉伸与收缩,占满空间; -
Expanded
:空间拉伸与收缩,占满空间,与Flexible
效果等价,区别在与使用Expanded
不用再设置fit: FlexFit.tight
,因为Expanded
在内部已经设置了,所以其使用更广; - 空间剩余时,会进行拉伸占满剩余空间;
- 空间不足时,会收缩子widget,占满空间;
class RowDemo2 extends StatelessWidget {
const RowDemo2({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
textBaseline: TextBaseline.alphabetic,
children: [
Flexible(
child: Container(width: 80,height: 60,color: Colors.red),
fit: FlexFit.tight,
),
Expanded(
child: Container(width: 80,height: 80,color: Colors.green),
),
Container(width: 80,height: 100,color: Colors.blue),
Container(width: 80,height: 120,color: Colors.orange),
],
);
}
}
这里补充个小知识点:
如果children中出现2个Flexible或者Expand,那么剩余空间会根据两个Expand的Flex比例来填充,如果没有设置Flex,那么默认的Flex值为1
Column组件
- Column的主轴方向为:竖直方向,交叉轴为水平方向;
- 与Row组件类似,只是子组件在主轴方向即竖直方向上排列;
Stack组件
- 在开发中,我们多个组件可能需要
重叠显示
,在Android中使用Frame布局实现,而在Flutter中我们需要使用层叠布局Stack
来实现; - 内部子组件默认从左上角开始排布;
- Stack默认的大小是包裹内容的尺寸大小;
- 参数alignment:表示从什么位置开始排布所有的子组件;
- 参数fit:expand表示将所有子组件拉伸到最大;
- 参数overflow:对于超出父组件区域的子组件的处理
- flip:超出部分被裁减;
- visible:超出部分依然显示; - 案例代码:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: SFHomePage());
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("基础widget")), body: SFHomeContent());
}
}
class SFHomeContent extends StatefulWidget {
@override
State createState() {
print("createState");
return _SFHomeContentState();
}
}
class _SFHomeContentState extends State {
@override
Widget build(BuildContext context) {
return StackDemo1();
}
}
class StackDemo2 extends StatelessWidget {
const StackDemo2({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
children: [
Container(
height: 200,
child: Image.asset("asset/images/180.png", fit: BoxFit.fill),
width: double.infinity),
Positioned(
child: Container(
child: Row(
children: [
Text("这是一行文本", style: TextStyle(fontSize: 17, color: Colors.white)),
IconButton(icon: Icon(Icons.favorite),color: Colors.white,onPressed: (){
})
],
mainAxisAlignment: MainAxisAlignment.spaceBetween,
),
color: Color.fromARGB(150, 0, 0, 0),
width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 8),
),
left: 0,
right: 0,
bottom: 0,
)
],
);
}
}
class StackDemo1 extends StatelessWidget {
const StackDemo1({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
children: [
Image.asset("asset/images/180.png"),
Positioned(
child: Container(
width: 100,
height: 100,
color: Colors.green,
),
right: 0,
),
Positioned(
child: Text("推客图标", style: TextStyle(fontSize: 20)),
left: 0,
)
],
alignment: AlignmentDirectional.bottomStart,
overflow: Overflow.visible,
);
}
}
- StackDemo1与StackDemo2的效果图如下:
- 滚动组件有
ListView
,GridView
,Sliver
ListView
- ListView创建的方式通常有三种,分别为
ListView()
,ListView.builder()
,ListView.separated()
ListView创建方式
- 第一种方式:
ListView()
- 案例代码:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: SFHomePage());
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("基础widget")), body: SFHomeContent());
}
}
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(
scrollDirection: Axis.vertical,
itemExtent: 100,//设置Item的高度
children: List.generate(100, (index) {
return ListTile(
leading: Icon(Icons.people),
trailing: Icon(Icons.delete),
title: Text("联系人${index+1}"),
subtitle: Text("联系人电话号码:19991604555"),
);
}),
);
}
}
-
ListTile
组件就是ListView的Item; -
itemExtent
:设置Item的高度; - 效果图如下:
通过
ListView()
创建,会一次性创建100个Item,这样性能比较差,其适用于Item个数确定,且数量较少的情况下才会采用;第二种方式:
ListView.builder()
ListView.builder()
不会一次性创建所有Item,而是需要展示的Item才会去创建,性能较好;案例代码如下:
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: 100,
itemExtent: 50,
itemBuilder: (BuildContext ctx, int index){
return Text("Hello World!!! ${index}",style: TextStyle(fontSize: 20),);
}
);
}
}
- 第三种方式:
ListView.separated()
- 案例代码如下:
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView.separated(
itemBuilder: (BuildContext ctx, int index){
return Text("Hello World!!! ${index}",style: TextStyle(fontSize: 20),);
},
//分割线
separatorBuilder: (BuildContext ctx,int index){
return Divider(color: Colors.red,indent: 20,endIndent: 20,thickness: 5);
},
itemCount: 100
);
}
}
-
itemBuilder
:创建Item; -
separatorBuilder
:创建分割线;
GridView
- GridView的创建方式有:
GridView()
,GridView.builder()
- 案例代码一:
GridView()
+SliverGridDelegateWithFixedCrossAxisCount
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 1.5,
crossAxisSpacing: 8,
mainAxisSpacing: 8
),
children:
List.generate(100, (index) {
return Container(
color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
);
}),
);
}
}
-
SliverGridDelegateWithFixedCrossAxisCount
:交叉轴方向上item数量固定,其宽度根据屏幕的宽度与item的数量进行计算;-
crossAxisCount
:item的个数; -
childAspectRatio
:item的宽高比; -
crossAxisSpacing
:交叉轴方向上 item之间的间距; -
mainAxisSpacing
:主轴方向上 item之间的间距;
-
案例代码二:
GridView()
+SliverGridDelegateWithMaxCrossAxisExtent
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GridView(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 220,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
childAspectRatio: 1.5
),
children: List.generate(100, (index) {
return Container(
color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
);
})
);
}
}
-
SliverGridDelegateWithMaxCrossAxisExtent
:交叉轴方向上的设置item宽度,个数不固定;-
maxCrossAxisExtent
:item的最大宽度;
-
案例代码三:
GridView.builder()
+SliverGridDelegateWithFixedCrossAxisCount
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 8,
mainAxisSpacing: 8
),
itemBuilder: (BuildContext ctx,int index){
return Container(
color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
);
}
);
}
}
Slivers -- CustomScrollView
CustomScrollView
:自定义滚动组件,需要传入Slivers
即Sliver数组,我们知道ListView
与GridView
都是继承自BoxScrollView
,而BoxScrollView
是一个抽象类,从源码来看ListView
与GridView
在察创建的过程中都需要执行buildSlivers
方法,其内部调用buildChildLayout
方法,这是一个抽象方法,分别由ListView
与GridView
来实现,最终提供一个Sliver数组
,其中ListView
提供的Sliver为SliverFixedExtentList
,GridView
提供的Sliver为SliverGrid
案例代码:单个Sliver
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: SFHomePage());
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("基础widget")),
body: SFHomeContent());
}
}
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SliverDemo1();
}
}
class SliverDemo1 extends StatelessWidget {
const SliverDemo1({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: [
SliverSafeArea(
sliver: SliverPadding(
padding: EdgeInsets.all(8),
sliver: SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
childAspectRatio: 1.5
),
delegate: SliverChildBuilderDelegate(
(BuildContext ctx,int index){
return Container(
color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
);
},
childCount: 100
),
),
),
)
],
);
}
}
自定义CustomScrollView,需传入Slivers数组,这里传入的Sliver为
SliverGrid
;参数
gridDelegate
:是提供布局信息;参数
delegate
:是提供item组件,类型为SliverChildDelegate
;-
SliverChildDelegate是抽象类,其作用是用来创建滚动组件的item,其有两个子类分别为
SliverChildListDelegate
与SliverChildBuilderDelegate
-
SliverChildListDelegate
:性能较差,item一次性创建所有; -
SliverChildBuilderDelegate
:性能较好,创建需要展示的item;
-
-
SafeArea
与SliverSafeArea
的区别:- SafeArea:安全区域,让目标组件在安全区域内显示;
- SliverSafeArea:给Sliver设置安全区域,且在滚动时可以在非安全区域内滚动,而SafeArea不可以;
SliverPadding
:是Sliver自己的设置内边距的组件;案例代码:多个Sliver
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: SFHomePage());
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(title: Text("基础widget")),
body: SFHomeContent());
}
}
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: [
SliverAppBar(
pinned: true,
expandedHeight: 200,
flexibleSpace: FlexibleSpaceBar(
title: Text("Hello World!!",style: TextStyle(fontSize: 25),),
),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
childAspectRatio: 2.5
),
delegate: SliverChildBuilderDelegate(
(BuildContext ctx,int index){
return Container(
color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
);
},
childCount: 10
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext ctx,int index){
return ListTile(
leading: Icon(Icons.people),
title: Text("联系人$index"),
);
},
childCount: 20
),
)
],
);
}
}
-
slivers
数组中传入了SliverAppBar
,SliverGrid
与SliverList
三种类型的Sliver,效果如下:
滚动组件的监听
- 滚动组件的监听通常有两种方式,分别为
controller
与NotificationListener
controller监听
- 可以设置默认值offset;
- 监听滚动,也可以监听滚动的位置;
- 案例代码:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: SFHomePage());
}
}
class SFHomePage extends StatefulWidget {
@override
_SFHomePageState createState() => _SFHomePageState();
}
class _SFHomePageState extends State {
ScrollController controller = ScrollController(initialScrollOffset: 300);
bool isShowFloatButton = false;
@override
void initState() {
super.initState();
controller.addListener(() {
print("监听到滚动: ${controller.offset}");
setState(() {
isShowFloatButton = controller.offset >= 1000;
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("基础widget")),
body: SFHomeContent(controller),
floatingActionButton: isShowFloatButton ? FloatingActionButton(
child: Icon(Icons.arrow_upward),
onPressed: (){
controller.animateTo(0, duration: Duration(seconds: 1), curve: Curves.easeIn);
},
) : null,
);
}
}
class SFHomeContent extends StatelessWidget {
final ScrollController controller;
SFHomeContent(this.controller);
@override
Widget build(BuildContext context) {
return ListView.builder(
controller: controller,
itemBuilder: (BuildContext ctx, int index) {
return ListTile(
leading: Icon(Icons.people),
title: Text("联系人$index"),
);
},
itemCount: 100,
);
}
}
- 右下角悬浮按钮,当前滚动偏移量>=1000时显示;
NotificationListener监听
- 案例代码:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: SFHomePage());
}
}
class SFHomePage extends StatefulWidget {
@override
_SFHomePageState createState() => _SFHomePageState();
}
class _SFHomePageState extends State {
bool isShowFloatButton = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("基础widget")),
body: SFHomeContent(),
floatingActionButton: isShowFloatButton
? FloatingActionButton(
child: Icon(Icons.arrow_upward),
onPressed: () {
},
)
: null,
);
}
}
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return NotificationListener(
onNotification: (ScrollNotification notification){
if(notification is ScrollStartNotification){
print("开始滚动");
}else if (notification is ScrollUpdateNotification){
print("正在滚动 -- 总区域:${notification.metrics.maxScrollExtent} 当前位置: ${notification.metrics.pixels}");
}else if (notification is ScrollEndNotification){
print("结束滚动");
}
return true;
},
child: ListView.builder(
itemBuilder: (BuildContext ctx, int index) {
return ListTile(
leading: Icon(Icons.people),
title: Text("联系人$index"),
);
},
itemCount: 100,
),
);
}
}
总结
现在对以上出现的所有Widget做一个总结,主要针对继承关系:
-
以
StatelessWidget
为中心的Widget组件有如下:
-
以
StatefulWidget
为中心的Widget组件有如下:
-
以
RenderObjectWidget
为中心的Widget组件有如下: