记录学习项目
在Flutter中,一切皆是Widget(组件),Widget的功能是“描述一个UI元素的配置数据”,它就是说,Widget其实并不是表示最终绘制在设备屏幕上的显示元素,而它只是描述显示元素的一个配置数据。
实际上,Flutter中真正代表屏幕上显示元素的类是 Element,也就是说Widget 只是描述 Element 的配置数据。并且一个 Widget 可以对应多个 Element,因为同一个 Widget 对象可以被添加到 UI树的不同部分,而真正渲染时,UI树的每一个 Element 节点都会对应一个 Widget 对象。
Flutter 完整页面 Widget:Material App
、Scaffold
、AppBar
🍝MaterialApp
🤡 表述:MaterialApp
是我们使用 Flutter
开发中最常用的符合 Material Design
设计理念的入口 Widget。你可以将它类比成为网页中的<html></html>
,且它自带路由、主题色,<title>
等功能
title
🤡 表述:Strig
类型,该属性会在 Android
应用管理器的 App 上方显示,对于 iOS 设备是没有效果的【一般来讲都是 APP 名字】
home
🤡 表述:Widget
类型,这是在应用程序正常启动时首先显示的 Widget
,除非指定了 initialRoute
。如果 initialRoute
显示失败,也该显示该 Widget
(进入程序后显示的第一个页面,传入的是一个 Widget
,但实际上这个 Widget
需要包裹一个 Scaffold
以显示该程序使用 Material Design
风格)
routes
🤡 表述:Map<String, WidgetBuilder>
类型,是应用的顶级路由表。当我们再使用 Navigator.pushNamed
进行命名路由的跳转时,会在此路表中进行查找并跳转。如果你的应用程序只有一个页面,则无需使用 routes
,直接指定 home
对应的 Widget
即可(参数以键值对的形式传递 key
:路由名字 value
:对应的 Widget
)
routes: {
"/HomePage": (context) => HomePage(),
"/AboutPage": (context) => AboutPage(),
},
theme
🤡 表述:应用程序的主题,各种的定制颜色都可以设置,用于程序主题切换
new ThemeData(
//主题色
primarySwatch: Colors.blue,
)
如果我们想要自定义一个 HEX 值,那么你可能会想到使用
primarySwatch: Color.fromARGB(a, r, g, b)
.不过这样是编译不过的。因为primarySwatch
是MarerialColor
类型,而刚才返回的是 Color 类型
color.dart 文件,我们需要把普通的比如 Hex color 转换成MaterialColor
import 'dart:ui';
import 'package:flutter/material.dart';
//调用的时候需要把hex改一下,比如#223344 needs change to 0xFF223344
//即把#换成0xFF即可
MaterialColor createMaterialColor(Color color) {
List strengths = <double>[.05];
Map swatch = <int, Color>{};
final int r = color.red, g = color.green, b = color.blue;
for (int i = 1; i < 10; i++) {
strengths.add(0.1 * i);
}
strengths.forEach((strength) {
final double ds = 0.5 - strength;
swatch[(strength * 1000).round()] = Color.fromRGBO(
r + ((ds < 0 ? r : (255 - r)) * ds).round(),
g + ((ds < 0 ? g : (255 - g)) * ds).round(),
b + ((ds < 0 ? b : (255 - b)) * ds).round(),
1,
);
});
return MaterialColor(color.value, swatch);
}
// 这样子就可以了
primarySwatch: createMaterialColor(Color(0xFF223344)),
这边可以看看ThemeData
可以设置那些主题
继承关系: Object -> Diagnosticable -> ThemeData
primarySwatch
是主题颜色的一个"样本色",通过这个样本色可以在一些条件下生成一些其它的属性,例如,如果没有指定 primaryColor,并且当前主题不是深色主题,那么 primaryColor 就会默认为 primarySwatch 指定颜色,还有一些相似的属性如 accentColor 、indicatorColor 等也会受 primarySwatch 影响
-
ThemeData(Color 类型属性)
-
accentColor - 前景色(文本、按钮等)。
-
backgroundColor - 与 primaryColor 对比的颜色(例如 用作进度条的剩余部分)。
-
bottomAppBarColor - BottomAppBar 的默认颜色。
-
buttonColor - Material 中 RaisedButtons 使用的默认填充色。
-
canvasColor - MaterialType.canvas Material 的默认颜色。
-
cardColor - Material 被用作 Card 时的颜色。
-
dialogBackgroundColor - Dialog 元素的背景色。
-
disabledColor - 用于 Widget 无效的颜色,无论任何状态。例如禁用复选框。
-
dividerColor - Dividers 和 PopupMenuDividers 的颜色,也用于 ListTiles 中间和 DataTables 的每行中。
-
errorColor - 用于输入验证错误的颜色,例如在 TextField 中。
-
highlightColor - 用于类似墨水喷溅动画或指示菜单被选中的高亮颜色。
-
hintColor - 用于提示文本或占位符文本的颜色,例如在 TextField 中。
-
indicatorColor - TabBar 中选项选中的指示器颜色。
-
primaryColor - App 主要部分的背景色(ToolBar,TabBar 等)。
-
primaryColorDark - primaryColor 的较暗版本。
-
primaryColorLight - primaryColor 的较亮版本。
-
scaffoldBackgroundColor - 作为 Scaffold 基础的 Material 默认颜色,典型 Material 应用或应用内页面的背景颜色。
-
secondaryHeaderColor - 有选定行时 PaginatedDataTable 标题的颜色。
-
selectedRowColor - 选中行时的高亮颜色。
-
splashColor - 墨水喷溅的颜色。
-
textSelectionColor - 文本字段中选中文本的颜色,例如 TextField。
-
textSelectionHandleColor - 用于调整当前文本的哪个部分的句柄颜色。
-
toggleableActiveColor - 用于突出显示切换 Widget(如 Switch,Radio 和 Checkbox)的活动状态的颜色。
-
unselectedWidgetColor - 用于 Widget 处于非活动(但已启用)状态的颜色。 例如,未选中的复选框。 通常与 accentColor 形成对比。
-
focusColor - 焦点获取时的颜色,例如,一些按钮焦点、输入框焦点。
-
hoverColor - 点击之后徘徊中的颜色,例如,按钮长按,按住之后的颜色。
-
cursorColor - 输入框光标颜色。
-
-
ThemeData(Theme 相关类型属性)
-
accentIconTheme - IconThemeData 类型,与突出颜色对照的图片主题。
-
accentTextTheme - TextTheme 类型,与突出颜色对照的文本主题。
-
chipTheme - ChipThemeData 类型,用于渲染 Chip 的颜色和样式。
-
buttonTheme - ButtonThemeData 类型,定义了按钮等控件的默认配置,像 RaisedButton 和 FlatButton。
-
primaryIconTheme - IconThemeData 类型,一个与主色对比的图片主题。
-
primaryTextTheme - TextThemeData 类型,一个与主色对比的文本主题。
-
iconTheme - IconThemeData 类型,与卡片和画布颜色形成对比的图标主题。
-
inputDecorationTheme - InputDecorationTheme 类型
-
InputDecorator,TextField 和 TextFormField 的默认 InputDecoration 值基于此主题。
-
sliderTheme - SliderThemeData 类型,用于渲染 Slider 的颜色和形状。
-
textTheme - TextTheme 类型,与卡片和画布对比的文本颜色。
-
toggleButtonsTheme - ToggleButtonsThemeData 类型,Flutter 1.9 全新组件 ToggleButtons 的主题。
-
tabBarTheme - TabBarTheme 类型,TabBar 的主题样式。
-
tooltipTheme - TooltipThemeData 类型,tooltip 提示的主题样式。
-
cardTheme - CardTheme 类型,卡片的主题样式。
-
pageTransitionsTheme - PageTransitionsTheme 类型,页面转场主题样式。
-
appBarTheme - AppBarTheme 类型,AppBar 主题样式。
-
bottomAppBarTheme - BottomAppBarTheme 类型,底部导航主题样式。
-
dialogTheme - DialogTheme 类型,对话框主题样式。
-
floatingActionButtonTheme - FloatingActionButtonThemeData 类型,FloatingActionButton 的主题样式,也就是 Scaffold 属性的那个。
-
cupertinoOverrideTheme - CupertinoThemeData 类型,cupertino 覆盖的主题样式。
-
snackBarTheme - SnackBarThemeData 类型,弹出的 snackBar 的主题样式。
-
bottomSheetTheme - BottomSheetThemeData 类型,底部滑出对话框的主题样式。
-
popupMenuTheme - PopupMenuThemeData 类型,弹出菜单对话框的主题样式。
-
bannerTheme - MaterialBannerThemeData 类型,Material 材质的 Banner 主题样式。
-
dividerTheme - DividerThemeData 类型,Divider 组件的主题样式,也就是那个横向线条组件。
-
-
ThemeData(其他类型属性)
-
accentColorBrightness - Brightness 类型,
-
accentColor 的亮度。 用于确定放置在突出颜色顶部的文本和图标的颜色(例如 - FloatingButton 上的图标)。
-
brightness - Brightness 类型,应用程序整体主题的亮度。 由按钮等 Widget 使用,以确定在不使用主色或强调色时要选择的颜色。
-
platform - TargetPlatform 类型,Widget 需要适配的目标类型。
-
splashFactory - InteractiveInkFeatureFactory 类型,定义 InkWall 和 InkResponse 生成的墨水喷溅的外观。
-
primaryColorBrightness - Brightness 类型,primaryColor 的亮度。
-
fontFamily - String 类型,字体样式。
-
applyElevationOverlayColor - bool 类型,是否应用 elevation 覆盖颜色。
-
materialTapTargetSize - MaterialTapTargetSize 类型,Chip 等组件的尺寸主题设置,如:设置为 MaterialTapTargetSize.shrinkWrap 时,clip 距顶部距离为 0;设置为 MaterialTapTargetSize.padded 时距顶部有一个距离。
-
colorScheme - ColorScheme 类型,scheme 组颜色,一组 13 种颜色,可用于配置大多数组件的颜色属性。
-
typography - Typography 类型,用于配置 TextTheme、- - primaryTextTheme 和 accentTextTheme 的颜色和几何文本主题值。
-
navigatorKey
在构建导航器时使用的键,navigatorKey 定义的是当前 APP 实例的 GlobalKey。GlobalKey 能够跨 Widget 访问状态。
navigatorKey.currentState 相当于 Navigator.of(context)
GlobalKey<NavigatorState> _navigatorKey=new GlobalKey()
new MaterialApp(
navigatorKey: _navigatorKey,
);
initialRoute
如果构建了导航器,则显示的第一个路由的名称,通常是 routes 中定义的一个项,会覆盖 home 的定义
return MaterialApp(
title: '应用名称',
theme: ThemeData(
primaryColor: Colors.redAccent
),
// 该 initialRoute 默认值为 /,若不指定该值,则须在routes中的某个 key 替换为 '/'
// 例如 'second': (BuildContext context) => SecondScreen() 改为 '/': (BuildContext context) => SecondScreen()
initialRoute: 'second',
routes: <String, WidgetBuilder>{
"third": (BuildContext context) => ThirdScreen(),
'first': (BuildContext context) => FirstScreen(),
'second': (BuildContext context) => SecondScreen()
},
);
onGenerateRoute
注意,onGenerateRoute 只会对命名路由生效。
当通过 Navigation.of(context).pushNamed 跳转路由时, 在 routes 查找不到时,会调用该方法
场景:假设我们要开发一个电商 APP,当用户没有登录时可以看店铺、商品等信息,但交易记录、购物车、用户个人信息等页面需要登录后才能看。为了实现上述功能,我们需要在打开每一个路由页前判断用户登录状态!如果每次打开路由前我们都需要去判断一下将会非常麻烦,那有什么更好的办法吗?答案是有!
MaterialApp 有一个 onGenerateRoute
属性,它在打开命名路由时可能会被调用,之所以说可能,是因为当调用 Navigator.pushNamed(...)
打开命名路由时,如果指定的路由名在路由表中已注册,则会调用路由表中的 builder 函数来生成路由组件;如果路由表中没有注册,才会调用 onGenerateRoute
来生成路由
有了 onGenerateRoute 回调,要实现上面控制页面权限的功能就非常容易:我们放弃使用路由表,取而代之的是提供一个 onGenerateRoute 回调,然后在该回调中进行统一的权限控制,如:
MaterialApp(
... //省略无关代码
onGenerateRoute:(RouteSettings settings){
return MaterialPageRoute(builder: (context){
String routeName = settings.name;
// 如果访问的路由页需要登录,但当前未登录,则直接返回登录页路由,
// 引导用户登录;其它情况则正常打开路由。
}
);
}
);
onUnknownRoute
当 onGenerateRoute
无法生成路由(initialRoute 除外)时调用
* 如果home首页指定了,routes里面就不能有'/'的根路由了,会报错,/指定的根路由就多余了
* 如果没有home指定具体的页面,那routes里面就傲有/来指定根路由
* 路由的顺序按照下面的规则来:
* 1、如果有home,就会从home进入
* 2、如果没有home,有routes,并且routes指定了入口'/',就会从routes的/进入
* 3、如果上面两个都没有,或者路由找不到,如果有 onGenerateRoute,就会进入生成的路由
* 4、如果连上面的生成路由也没有,就会走到onUnknownRoute,不明所以的路由,比如网络连接失败,可以进入断网的页面
navigatorObservers
导航路由在跳转时的回调,比如 push,pop,remove,replace 是,可以拿到当前路由和后面路由的信息。 route.settings.name 可以拿到路由的名字
builder
当构建一个 Widget 前调用 一般做字体大小,方向,主题颜色等配置
onGenerateTitle
如果非空,则调用此回调函数来生成应用程序的标题字符串,否则使用标题。如果想根据区域显示不同的描述使用 onGenerateTitle(内部回调有 context)
MaterialApp(
title: '老孟',
onGenerateTitle: (context) {
var local = Localizations.localeOf(context);
if (local.languageCode == 'zh') {
return '老孟';
}
return 'laomeng';
},
...
)
locale
当前区域,如果为 null 则使用系统区域 一般用于语言切换
locale、localizationsDelegates、localeListResolutionCallback、localeResolutionCallback、supportedLocales 是区域设置和国际化相关的参数,如果 App 支持多国语言,那么就需要设置这些参数,默认情况下,Flutter 仅支持美国英语,如果想要添加其他语言支持则需要指定其他 MaterialApp 属性,并引入 flutter_localizations 包,到 2019 年 4 月,flutter_localizations 包已经支持 52 种语言,如果你想让你的应用在 iOS 上顺利运行,那么你还必须添加“flutter_cupertino_localizations”包
localizationsDelegates
本地化委托,用于更改 Flutter Widget 默认的提示语,按钮 text 等
localeListResolutionCallback
当传入的是不支持的语种,可以根据这个回调,返回相近,并且支持的语种
new MaterialApp(
localeResolutionCallback: (local,support){
if(support.contains(support)){
print('support');
return local;
}
print('no_support');
return const Locale('us','uk');
},
//这个代码是随便填的,有可能出错
locale: Locale('ze','cn'),
);
supportedLocales
传入支持的语种数组
new MaterialApp(
supportedLocales: [
const Locale('uok'),
const Locale('meg'),
],
);
localeResolutionCallback
localeResolutionCallback
和 localeListResolutionCallback
的区别是 localeResolutionCallback
返回的第一个参数是当前语言的 Locale
,而 localeListResolutionCallback
返回当前手机支持的语言集合
debugShowMaterialGrid
打开绘制基线网格材质应用程序的网格纸覆盖
showPerformanceOverlay
当为 true 时应用程序顶部覆盖一层 GPU 和 UI 曲线图,可即时查看当前流畅度情况
checkerboardRasterCacheImages
打开栅格缓存图像的棋盘格
checkerboardOffscreenLayers
打开渲染到屏幕外位图的图层的棋盘格
showSemanticsDebugger
当为 true 时,打开 Widget 边框,类似 Android 开发者模式中显示布局边界
debugShowCheckedModeBanner
在选中模式下打开一个小的“DEBUG”横幅,表示应用程序处于选中模式
🍝appBar
AppBar Material风格应用栏,有工具栏和其他的Widget构成 应用栏通常用于Scaffold.appBar属性,该属性将应用栏放置在屏幕顶部的固定高度小部件中。对于可滚动的应用栏,请参阅SliverAppBar,它将一个AppBar嵌入到一个条子中,以便在CustomScrollView中使用
在Flutter中,AppBar的布局主要由三个组件组成:leading
、title
和actions
。leading
位于AppBar的最左边;title
和actions
出现在右边。
leading
在title组件前面的组件,可以被分配任何内容——文本、图标,甚至一行中有多个小部件。
AppBar(
leading: Icon(Icons.account_circle_rounded),
),
如果没有提供leading, AppBar会自动为我们出现该出现的widget。示例包括返回到上一页的导航箭头或打开抽屉的菜单图标。
当之前的路由可用时,导航箭头自动出现。
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: TextButton(
child: Text('Push'),
onPressed: () => Navigator.push(context, MaterialPageRoute(
builder: (context) {
return SecondPage();
},
)),
),
),
);
}
}
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
);
}
}
当我们向Scaffold添加一个Drawer时,一个菜单图标被分配用于leading,为了打开该抽屉。
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
drawer: Drawer(),
);
}
}
可以利用属性automaticallyImplyLeading
来阻止这种行为
leadingWidth
默认
AppBar(
leading: Icon(Icons.account_circle_rounded),
leadingWidth: 100, // default is 56
),
automaticallyImplyLeading
配合leading使用,取决于automaticallyImplyLeading == true,存在上一页路由或者抽屉的话,会自动出现相对应的ICON已经事件,false会阻止这种行为
title
appBar的主要部件,类型为Widget,它主要用于显示标题,如应用程序标题或页面标题。
AppBar(
title: Text('Profile Page'),
),
但你并不局限于此,因为title也包含一个widget
。您可以使用它来显示图标、图像、形状,或使用布局小部件(如行和列)来显示这些内容的任何组合。
AppBar(
title: Container(
width: 40,
child: Image.network('https://book.flutterchina.club/logo.png'),
),
),
默认情况下,标题对齐到AppBar的中间,根据Material指南。你可以改变它,让它在中间左边:
AppBar(
centerTitle: false
)
titleSpacing
当centerTitle
为false,也就是标题在左边的时候titleSpacing
就会生效
默认
centerTitle
应该是居中的center
有限度,太大就会失效
当
action
占据足够宽的时候,标题也会居左[这个时候centerTitle
也是有用的]
AppBar(
centerTitle: false,
titleSpacing:100
)
actions
title之后显示的部件,对齐到AppBar右侧的widget
列表。我们经常在应用中看到它们作为按钮来触发下拉菜单、个人头像等。
AppBar(
actions: [
Container(
width: 30,
child: Image.network('https://book.flutterchina.club/logo.png'),
),
Icon(Icons.more_vert),
]
)
现在我们已经熟悉了AppBar的布局,让我们通过使用主题选项来进行定制。AppBar包含各种属性,包括颜色、大小、图标主题、文本主题等。
backgroundColor
AppBar(
backgroundColor: Colors.teal
)
iconTheme
icon主题设置
AppBar(
iconTheme: IconThemeData(color: Colors.green, size: 36),
actionsIconTheme: IconThemeData(color: Colors.red, size: 36)
)
actionsIconTheme
会替换iconTheme
在action
上的样式
textTheme
设置文本的主题
AppBar(
textTheme: TextTheme(
headline1: TextStyle(fontSize: 72.0, fontWeight: FontWeight.bold),
headline6: TextStyle(fontSize: 36.0, fontStyle: FontStyle.italic),
bodyText2: TextStyle(fontSize: 14.0, fontFamily: 'Hind'),
)
)
headline6
用于flutter的AppBar
和dialogs
中的的主要文本(例如 AppBar.title and AlertDialog.title)。--下图右上角没有变化
elevation
控制下方阴影栏的坐标
AppBar(
elevation: 102,
)
primary
app bar
是否显示在屏幕顶部。
如果为true,app bar
的工具栏元素和[bottom]小部件将被系统状态栏的高度填充在顶部。[flexibleSpace]的布局不受[primary]属性的影响。
shadowColor
AppBar(
shadowColor: Colors.red,
elevation: 102,
)
toolbarHeight
导航栏的高度,默认是56
AppBar(
toolbarHeight: 120
)
toolbarOpacity
appBar导航栏的透明程度。值1.0是完全不透明的,值0.0是完全透明的
AppBar(
toolbarOpacity: 0.5
)
flexibleSpace
类似于自由人,默认出现在右上角
AppBar(
title: Text('你三十岁'),
flexibleSpace: Text('你三十岁'),
)
bottom
PreferredSize
类型
可以看做在appbar的下面再加一块
AppBar(
bottom: PreferredSize(
preferredSize: const Size.fromHeight(48.0),
child: Theme(
data: Theme.of(context).copyWith(accentColor: Colors.white),
child: Container(
height: 48.0, alignment: Alignment.center, child: Text('你三十岁')),
),
)
)
PreferredSize
自定义底部
此控件不对其子控件施加任何约束,并且不以任何方式影响孩子的布局。
此控件对自定义AppBar.bottom和AppBar非常有用
自定义AppBar,也可以直接设置AppBar的高度(PreferredSize子控件为AppBar)
appBar: PreferredSize(
preferredSize: Size.fromHeight(200),
child: Container(
color: Colors.blue,
),
)
bottomOpacity
bottom部分的的透明度(不多表述)
shape
形状
AppBar(
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.circular(18))
)
brightness
状态栏字体颜色
- light:黑色
- dark:白色
AppBar(
brightness: Brightness.light,
)
backwardsCompatibility
不重要的属性--默认为false(官方不推荐使用吧 舍弃)
foregroundColor
应用栏材质的亮度
primary
appbar是否显示在任务栏顶部
🍝Scaffold
Scaffold 实现了基本的 Material 布局。只要是在 Material 中定义了的单个界面显示的布局控件元素,都可以使用 Scaffold 来绘制。
提供展示抽屉(drawers,比如:左边栏)、通知(snack bars) 以及 底部按钮(bottom sheets)。
我们可以将 Scaffold 理解为一个布局的容器。可以在这个容器中绘制我们的用户界面
appBar
页面上方导航条--这个就可以直接看上面的AppBar
body
页面容器--直接布局走起来(下一步就是研究布局)
floatingActionButton
悬浮的按钮
Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: ()=>{},
tooltip: 'Increment',
child: Icon(Icons.add),
)
)
研究一下FloatingActionButton
- child
子控件,通常为 Icon
FloatingActionButton(
child: Icon(Icons.add),
)
- tooltip
长按的结束时候会出现提示语
FloatingActionButton(
tooltip:"牛逼吧",
child: Icon(Icons.add),
)
- foregroundColor
Icon 与 Text 颜色
FloatingActionButton(
tooltip:"牛逼吧",
foregroundColor: Colors.green,
child: Icon(Icons.add),
)
其他的属性直接看这里[基本都是样式]:www.jianshu.com/p/6ced97ac7…
floatingActionButtonLocation : 悬浮按钮位置
Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: FloatingActionButton(
onPressed: ()=>{},
tooltip: 'Increment',
child: Icon(Icons.add),
)
)
FloatingActionButtonLocation
有很多个位置属性
- centerDocked:底部中间偏下
- centerFloat:底部中间偏上
- centerTop:顶部中间
- endDocked:右下角偏下
- endFloat:右下角偏上[ 默认 ]
- endTop:右上角偏上
- startDocked:左下角偏下
- startFloat:左下角偏上
- startTop:左上角偏上
以上所有的前面加一个mini就是全是小一点的,例如miniEndDocked
记得驼峰
floatingActionButtonAnimator
悬浮按钮动画
Scaffold(
floatingActionButtonAnimator: FloatingActionButtonAnimator.scaling,
floatingActionButton: FloatingActionButton(
onPressed: ()=>{},
tooltip: 'Increment',
child: Icon(Icons.add),
)
)
persistentFooterButtons
在底部呈现一组button,显示于[bottomNavigationBar]之上,[body]之下
它还有一个特性是,会受到Scaffold设置的颜色之类属性的影响。准确来说,是因为persistentFooterButtons只接收一个数组,所以它的任何自身的属性,都是受到Scaffold的其它属性影响的。
- drawer : 左侧菜单
Scaffold(
drawer: Drawer(),
)
chensong.blog.csdn.net/article/det…
- endDrawer : 右侧菜单
Scaffold(
drawer: Drawer(),
)
-
bottomNavigationBar : 底部导航TabBar
-
bottomSheet : 一个持久停留在body下方,底部控件上方的控件
-
backgroundColor : 背景色
-
resizeToAvoidBottomInset : 控制界面内容 body 是否重新布局来避免底部被覆盖了,比如当键盘显示的时候,重新布局避免被键盘盖住内容。默认值为 true。
-
primary : 是否在屏幕顶部显示Appbar, 默认为 true,Appbar 是否向上延伸到状态栏,如电池电量,时间那一栏
-
drawerDragStartBehavior : 控制 drawer 的一些特性
-
extendBody : body 是否延伸到底部控件
-
extendBodyBehindAppBar : 默认 false,为 true 时,body 会置顶到 appbar 后,如appbar 为半透明色,可以有毛玻璃效果
-
drawerScrimColor : 遮罩颜色
-
drawerEdgeDragWidth : 侧滑栏拉出来的宽度
-
drawerEnableOpenDragGesture : 左侧侧滑栏是否可以滑动
-
endDrawerEnableOpenDragGesture : 右侧侧滑栏是否可以滑动
三个Button
,TextButton
、OutlinedButton
、ElevatedButton
🍝TextButton
TextButton(
child: Text('牛逼'),
onPressed: () {},
onLongPress : () {}
)
🍝OutlinedButton
OutlinedButton(
child: Text("牛逼"),
onPressed: () {},
onLongPress : () {}
)
🍝ElevatedButton
ElevatedButton(
child: Text('牛逼'),
onPressed: () {},
onLongPress : () {}
)
样式[以ElevatedButton
为例]
首先先解决MaterialStateProperty
问题
ButtonStyle的样式都需要MaterialStateProperty去包裹
-
MaterialStateProperty.all
是包裹
value
返回组件需要的样式 -
MaterialStateProperty.resolveAs
[辅助函数]看源码得知他首先回去判断传进来的值是否是
MaterialStateProperty.all
包裹之后的值,如果是那就解套返回最初是的value
,如果不是,那就直接返回 -
MaterialStateProperty.resolveWith
这个就是解套的方法,把
MaterialStateProperty.all
包裹的值再一次接套返回最初的value
-
textStyle // 字体
ElevatedButton( child: Text("牛逼"), style: ButtonStyle( textStyle: MaterialStateProperty.all(TextStyle(fontSize: 36)), ), onPressed: () {}, )
-
fixedSize // 按钮的大小。这个大小仍然受到样式的minimumSize的限制
ElevatedButton( child: Text("牛逼"), style: ButtonStyle( fixedSize: MaterialStateProperty.all(Size(50, 20)), minimumSize: MaterialStateProperty.all(Size(300, 100))), onPressed: () {}, )
-
backgroundColor // 背景色
ElevatedButton( child: Text("牛逼"), style: ButtonStyle( backgroundColor: MaterialStateProperty.all(Color(0xff2DF8F2))), ), onPressed: () {}, )
-
foregroundColor // 字体颜色
ElevatedButton( child: Text("牛逼"), style: ButtonStyle( foregroundColor: MaterialStateProperty.all(Color(0xff000000)),), ), onPressed: () {}, )
-
overlayColor // 高亮色,按钮处于focused, hovered, or pressed时的颜色
ElevatedButton( child: Text("牛逼"), style: ButtonStyle( overlayColor: MaterialStateProperty.all(Color(0xff31C27C)) ,), ), onPressed: () {}, )
-
shadowColor // 阴影颜色
点击的时候会更加显眼
ElevatedButton( child: Text("牛逼"), style: ButtonStyle( shadowColor: MaterialStateProperty.all(Colors.red)),), ), onPressed: () {}, )
-
elevation // 阴影值
一层阴影(shadowColor设置这层阴影的颜色)
ElevatedButton( child: Text("牛逼"), style: ButtonStyle( elevation: MaterialStateProperty.all(5),), ), onPressed: () {}, )
-
padding // padding
ElevatedButton( child: Text("牛逼"), style: ButtonStyle( padding: MaterialStateProperty.all(EdgeInsets.all(36)),), ), onPressed: () {}, )
-
minimumSize // 最小尺寸
ElevatedButton( child: Text("牛逼"), style: ButtonStyle( minimumSize: MaterialStateProperty.all(Size(300, 100)), ), onPressed: () {}, )
-
side // 边框
ElevatedButton( child: Text("牛逼"), style: ButtonStyle( side: MaterialStateProperty.all( BorderSide(width: 4, color: Color(0xffffffff))), //边框 ), onPressed: () {}, )
-
shape // 形状
菱形
ElevatedButton( child: Text("牛逼"), style: ButtonStyle( shape: MaterialStateProperty.all(BeveledRectangleBorder( borderRadius: BorderRadius.circular(8))), //圆角弧度 ), onPressed: () {}, )
圆形
ElevatedButton( child: Text("牛逼"), style: ButtonStyle( shape: MaterialStateProperty.all(CircleBorder( side: BorderSide( style: BorderStyle.none, ))), //圆角弧度 ), onPressed: () {}, )
-
mouseCursor // 鼠标指针的光标进入或悬停在此按钮的[InkWell]上时(这个用于Web端或PC端)
-
visualDensity // 按钮布局的紧凑程度
可以扩展上下左右距离
ElevatedButton( child: Text("牛逼"), style: ButtonStyle( visualDensity: VisualDensity( horizontal: 2.0, vertical: 2.0), //set the button's visual density ), onPressed: () {}, )
-
tapTargetSize // 响应触摸的区域
置MaterialTapTarget大小。可以设置为:值,填充和收缩包装属性
MaterialTapTargetSize有2个值,分别为:
- padded:最小点击区域为48*48。[实际大小小于48,依旧以48算]
- shrinkWrap:子组件的实际大小。
ElevatedButton( child: Text("牛逼"), style: ButtonStyle( minimumSize: MaterialStateProperty.all(Size(20, 20)), tapTargetSize: MaterialTapTargetSize.padded), onPressed: () {}, )
下图高度没过48--只有20,按钮下面也可以触发按钮
-
animationDuration // [shape]和[elevation]的动画更改的持续时间。
animationDuration: Duration.zero
-
enableFeedback // 检测到的手势是否应提供声音和/或触觉反馈。例如,在Android上,点击会产生咔哒声,启用反馈后,长按会产生短暂的振动。通常,组件默认值为true。
-
alignment // 设置按钮
child
的Alignment
alignment: Alignment.bottomCenter
styleFrom 会构造出 ButtonStyle
无状态组件StatelessWidget
和有状态组件StatefulWidget
StatelessWidget
Flutter中的StatelessWidget是一个不需要状态更改的widget - 它没有要管理的内部状态。(有点React的意思啊---其实就是受控组件(外部给的状态)和非受控组件(内部自己管理状态))
Icon
、ImageIcon
、Dialog
、AboutDialog
, CircleAvatar
和 Text
等都是StatelessWidget
的子类
class TextRed extends StatelessWidget {
final String text;
const TextRed({Key? key, required this.text}) : super(key: key);
@override
Widget build(BuildContext context) {
return Text(
text,
style: TextStyle(color: Colors.red),
);
}
}
TextRed
从外部接受一个数据源text并将它显示。
无状态的组件的声明周期只有一个:build,它只会在三种情况下被调用:
- 将widget插入树中的时候,也就是第一次构建(初始化的时候)
- 当widget的父级更改了其传入的值时,例如,
TextRed
的父类改变了text的值 - 当它依赖的InheritedWidget发生变化时(InheritedWidget打一个问号---著名的 Provider 状态管理框架也是基于 InheritedWidget 实现的,因此不管是工作中,还是面试,InheritedWidget 组件的原理及使用场景都是考察的重点。)
使用Statelesswidget更轻量,更节省内存资源。初始化Statelesswidget的时候不会附带一些动态更新UI的方法,这样也会提升我们软件的性能。
StatefulWidget
StatefulWidget 是可变状态的widget。 使用setState
方法管理StatefulWidget
的状态的改变。调用setState告诉Flutter框架,某个状态发生了变化,Flutter会重新运行build方法,以便应用程序可以应用最新状态。
它实现了一个setState方法,当我们调用这个方法的时候,该Statefulwidget会被重新渲染,注意是重新被渲染,而不是局部更新。 当我们调用setState时,Flutter在收到该消息后,会重新调用其build方法重新构建这个widget,从而达到更新UI的目的。
Checkbox
, Radio
, Slider
, InkWell
, Form
, 和 TextField
等都是有状态的widget,也是StatefulWidget的子类。
class StatefulWidgetDemoPage extends StatefulWidget {
@override
_StatefulWidgetDemoPageState createState() => _StatefulWidgetDemoPageState();
}
class _StatefulWidgetDemoPageState extends State<StatefulWidgetDemoPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {});
},
child: Icon(Icons.add),
),
appBar: AppBar(
title: Text("StatefuleWidget Demo"),
centerTitle: true,
backgroundColor: Colors.blue,
),
body: Column(
children: [
Container(
width: 100,
height: 100,
margin: EdgeInsets.all(10),
/// 颜色一个随机值
color: _randomColor(),
),
],
),
);
}
/// 获取一个随机的颜色值
_randomColor() {
return Color.fromARGB(255, Random().nextInt(255), Random().nextInt(255),
Random().nextInt(255));
}
}
我们定义了一个生成随机颜色的方法_randomColor(),它会返回一个Color对象,然后我们又定义了一个Container,Container的初始化参数color的值是_randomColor()的返回值。然后我们在FloatingActionButton的onPressed的方法中调用一下setState方法,这个时候Flutter会重新绘制StatefulWidgetDemoPage,所以每次点击按钮,我们可以看到Container的颜色都是不一样的。
布局
线性布局
所谓线性布局,即指沿水平或垂直方向排布子组件。Flutter中通过Row和Column来实现线性布局,类似于Android中的LinearLayout控件。Row和Column都继承自Flex
Row 水平布局----Column类推
- children
子widget
的数组
Drawer(
child: Row(children: <Widget>[
Text('This is Drawer'),
Text('This is Drawer'),
Text('This is Drawer'),
]),
)
- textDirection
表示水平方向子组件的布局顺序(是从左往右还是从右往左),默认为系统当前Locale环境的文本方向(如中文、英语都是从左往右,而阿拉伯语是从右往左)
Drawer(
Row(
textDirection: TextDirection.rtl,
children: <Widget>[
Text('This'),
Text(' is '),
Text('Drawer'),
]),
)
)
- mainAxisAlignment
表示子组件在Row所占用的水平空间内对齐方式,textDirection是mainAxisAlignment的参考系。
Row(
textDirection:ltr,
// MainAxisAlignment.end:右对齐
// MainAxisAlignment.center:居中
// MainAxisAlignment.start:左对齐
mainAxisAlignment: MainAxisAlignment.end
)
Row(
textDirection:rtl,
// MainAxisAlignment.end:左对齐
// MainAxisAlignment.center:居中
// MainAxisAlignment.start:右对齐
mainAxisAlignment: MainAxisAlignment.end
)
-
verticalDirection
表示Row纵轴(垂直)的对齐方向,默认是VerticalDirection.down,表示从上到下。
-
mainAxisSize
表示Row在主轴(水平)方向占用的空间,默认是MainAxisSize.max,表示尽可能多的占用水平方向的空间,此时无论子widgets实际占用多少水平空间,Row的宽度始终等于水平方向的最大宽度;而MainAxisSize.min表示尽可能少的占用水平空间,当子组件没有占满水平剩余空间,则Row的实际宽度等于所有子组件占用的的水平空间;
- crossAxisAlignment
类似于MainAxisAlignment
,这个指的是纵轴
- textBaseline
文本的基准线
弹性布局
弹性布局允许子组件按照一定比例来分配父容器空间。弹性布局的概念在其它UI系统中也都存在,如H5中的弹性盒子布局,Android中的FlexboxLayout等。Flutter中的弹性布局主要通过Flex和Expanded来配合实现
Flex
Flex组件可以沿着水平或垂直方向排列子组件,如果你知道主轴方向,使用Row或Column会方便一些,因为Row和Column都继承自Flex,参数基本相同,所以能使用Flex的地方基本上都可以使用Row或Column。Flex本身功能是很强大的,它也可以和Expanded组件配合实现弹性布局。接下来我们只讨论Flex和弹性布局相关的属性
- direction 弹性布局的方向, Row默认为水平方向,Column默认为垂直方向
Flex(
direction: Axis.horizontal,
)
Flex(
direction: Axis.vertical,
)
- children:子组件数组
Expanded
类似于html的弹性属性
可以按比例“扩伸” Row、Column和Flex子组件所占用的空间。
flex参数为弹性系数,如果为0或null,则child是没有弹性的,即不会被扩伸占用的空间。如果大于0,所有的Expanded按照其flex的比例来分割主轴的全部空闲空间
Flex(
direction: Axis.horizontal,
children: <Widget>[
Expanded(
flex: 1,
child: Container(
width: 301,
height: 30.0,
color: Colors.red,
),
),
Expanded(
flex: 2,
child: Container(
height: 30.0,
color: Colors.green,
),
),
],
)
这一块打算后面再看
流式布局
解决flex布局文本超过边界问题
Wrap
除了超出显示范围后Wrap会折行外,其它行为基本Row
相同
Wrap
的特有属性
- spacing
主轴方向子widget的间距
Padding(
padding: EdgeInsets.only(top: 100),
child: Wrap(
spacing: 58.0, // 主轴(水平)方向间距
alignment: WrapAlignment.center, //沿主轴方向居中
children: <Widget>[
new Chip(
avatar: new CircleAvatar(
backgroundColor: Colors.blue, child: Text('A')),
label: new Text('Hamilton'),
),
new Chip(
avatar: new CircleAvatar(
backgroundColor: Colors.blue, child: Text('M')),
label: new Text('Lafayette'),
),
new Chip(
avatar: new CircleAvatar(
backgroundColor: Colors.blue, child: Text('H')),
label: new Text('Mulligan'),
),
new Chip(
avatar: new CircleAvatar(
backgroundColor: Colors.blue, child: Text('J')),
label: new Text('Laurens'),
),
],
),
)
- runSpacing
纵轴方向的间距
放到
Flex
容器里面会失效
Wrap(
runSpacing: 68.0, // 纵轴方向的间距
children: [
// .....
],
)
- runAlignment
纵轴方向的对齐方式
Wrap(
alignment: WrapAlignment.end, //沿主轴方向右对齐
children: [
// .....
],
)
Flow
Flow的使用挺复杂,需要自己实现子widget的位置转换,在很多场景下首先要考虑的是Wrap是否满足需求。Flow主要用于一些需要自定义布局策略或性能要求较高(如动画中)的场景。Flow有如下优点:
-
性能好;Flow是一个对子组件尺寸以及位置调整非常高效的控件,Flow用转换矩阵在对子组件进行位置调整的时候进行了优化:在Flow定位过后,如果子组件的尺寸或者位置发生了变化,在FlowDelegate中的paintChildren()方法中调用context.paintChild 进行重绘,而context.paintChild在重绘时使用了转换矩阵,并没有实际调整组件位置。
-
灵活;由于我们需要自己实现FlowDelegate的paintChildren()方法,所以我们需要自己计算每一个组件的位置,因此,可以自定义布局策略。
class MyFlowDelegate extends FlowDelegate {
@override
void paintChildren(FlowPaintingContext context) {
/*屏幕宽度*/
var screenW = context.size.width;
double padding = 5; //间距
double offsetX = padding; //x坐标
double offsetY = padding; //y坐标
for (int i = 0; i < context.childCount; i++) {
/*如果当前x左边加上子控件宽度小于屏幕宽度 则继续绘制 否则换行*/
if (offsetX + boxSize < screenW) {
/*绘制子控件*/
context.paintChild(i,
transform: Matrix4.translationValues(offsetX, offsetY, 0));
/*更改x坐标*/
offsetX = offsetX + boxSize + padding;
} else {
/*将x坐标重置为margin*/
offsetX = padding;
/*计算y坐标的值*/
offsetY = offsetY + boxSize + padding;
/*绘制子控件*/
context.paintChild(i,
transform: Matrix4.translationValues(offsetX, offsetY, 0));
}
}
}
@override
bool shouldRepaint(FlowDelegate oldDelegate) {
return true;
}
}
Flow(
delegate: MyFlowDelegate(),
children: <Widget>[
new Container(
width: 80.0,
height: 80.0,
color: Colors.red,
),
new Container(
width: 80.0,
height: 80.0,
color: Colors.green,
),
new Container(
width: 80.0,
height: 80.0,
color: Colors.blue,
),
],
)
层叠布局 Stack、Positioned
按照代码中声明的顺序,允许子组件堆叠起来(子组件可以根据距父容器四个角的位置来确定自身的位置)
层叠布局和Web中的绝对定位、Android中的Frame布局是相似的
Flutter 中是使用Stack和Positioned这两个组件来配合实现绝对定位
-
alignment
此参数决定如何去对齐没有定位(没有使用Positioned)或部分定位的子组件。
所谓部分定位,在这里特指没有在某一个轴上定位[没有left、right、top、bottom其中一个属性]:left、right为横轴,top、bottom为纵轴,只要包含某个轴上的一个定位属性就算在该轴上有定位。
Stack( alignment:Alignment.center , //指定未定位或部分定位widget的对齐方式 children: <Widget>[ Container(child: Text("Hello world",style: TextStyle(color: Colors.white)), color: Colors.red, ), Positioned( left: 18.0, child: Text("I am Jack"), ), Positioned( top: 18.0, child: Text("Your friend"), ) ], )
我们可以看到
Your friend
顶部18的中间位置,I am Jack
左边18的中间位置,Hello world
在正中间位置--没毛病
对齐与相对定位
Center
居中
Center(
child: Text('Hello Flutter')
),
Align
(对齐布局)
- alignment = Alignment.center:
- widthFactor:宽度因子
- heightFactor:高度因子
Align(
alignment: FractionalOffset.topRight,
child: Text('Hello'),
)
widthFactor和heightFactor是用于确定Align 组件本身宽高的属性;它们是两个缩放因子,会分别乘以子元素的宽、高,最终的结果就是Align 组件的宽高
Align(
widthFactor: 2,
heightFactor: 2,
alignment: Alignment.topRight,
child: FlutterLogo(
size: 60,
),
)
因为FlutterLogo的宽高为60,则Align的最终宽高都为2*60=120
- Alignment 属性
它有两个常用的子类:Alignment和 FractionalOffset
- 内置的位置属性
topLeft = Alignment(-1.0, -1.0)
topCenter = Alignment(0.0, -1.0)
topRight = Alignment(1.0, -1.0)
centerLeft = Alignment(-1.0, 0.0)
center = Alignment(0.0, 0.0)
centerRight = Alignment(1.0, 0.0)
bottomLeft = Alignment(-1.0, 1.0)
bottomCenter = Alignment(0.0, 1.0)
bottomRight = Alignment(1.0, 1.0)
- 自定义
(Alignment.x*childWidth/2+childWidth/2, Alignment.y*childHeight/2+childHeight/2)
Alignment(-0.5,0.5)
- 实例
Align(
widthFactor: 2,
heightFactor: 2,
alignment: Alignment(2,0.0),
child: FlutterLogo(
size: 60,
),
)
将Alignment(2,0.0)带入上述坐标转换公式,可以得到FlutterLogo的实际偏移坐标为(90,30)
- FractionalOffset
FractionalOffset 继承 Alignment,他们 2 个区别就是坐标系不一样,Alignment 的原点是中心,而 FractionalOffset 原点是左上角。
- 内置的位置属性
topLeft = FractionalOffset(0.0, 0.0)
topCenter = FractionalOffset(0.5, 0.0)
topRight = FractionalOffset(1.0, 0.0)
centerLeft = FractionalOffset(0.0, 0.5)
center = FractionalOffset(0.5, 0.5)
centerRight = FractionalOffset(1, 0.5)
bottomLeft = FractionalOffset(0.0, 1.0)
bottomCenter = FractionalOffset(0.5, 1.0)
bottomRight = FractionalOffset(1.0, 1.0)
- 自定义
实际偏移 = (FractionalOffse.x * childWidth, FractionalOffse.y * childHeight)
FractionalOffset(0.5,0.5)
- 实例
Container(
height: 120.0,
width: 120.0,
color: Colors.blue[50],
child: Align(
alignment: FractionalOffset(0.2, 0.6),
child: FlutterLogo(
size: 60,
),
),
)
将FractionalOffset(0.2, 0.6)带入坐标转换公式得FlutterLogo实际偏移为(12,36)
- AlignmentDirectional
AlignmentDirectional 的坐标系和 Alignment 比较像,原点在中心,不过 AlignmentDirectional 的起始位置和书写(TextDirection)方向有关
TextDirection可以决定AlignmentDirectional坐标系
同上
容器(常用的盒子)
Padding
Padding 在 Flutter 中用的也挺多的,作为一个基础的控件,功能非常单一,给子节点设置 padding 属性。写过其他端的都了解这个属性,就是设置内边距属性,内边距的空白区域,也是 widget 的一部分。
Flutter 中并没有单独的 Margin 控件,在 Container 中有 margin 属性,看源码关于 margin 的实现。
if (margin != null)
current = new Padding(padding: margin, child: current);
EdgeInsets
Padding(
//上下左右各添加16像素补白
padding: EdgeInsets.all(16.0),
child:Text("Hello world"),
)
Padding(
//左边添加8像素补白
padding: const EdgeInsets.only(left: 8.0),
child: Text("Hello world"),
),
Padding(
//上下各添加8像素补白
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Text("Hello world"),
)
Padding(
//左右各添加8像素补白
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text("Hello world"),
)
Padding(
//分别指定四个方向的补白
padding: const EdgeInsets.fromLTRB(20.0,.0,20.0,20.0),
child: Text("Hello world"),
)
尺寸容器
-
ConstrainedBox:适用于需要设置最大/小宽高,组件大小以来子组件大小,但不能超过设置的界限。
-
UnconstrainedBox:用到情况不多,当作- ConstrainedBox的子组件可以“突破”ConstrainedBox的限制,超出界限的部分会被截取。
-
SizedBox:适用于固定宽高的情况,常用于当作2个组件之间间隙组件。
-
AspectRatio:适用于固定宽高比的情况。
-
FractionallySizedBox:适用于占父组件百分比的情况。
-
LimitedBox:适用于没有父组件约束的情况。
-
Container:适用于不仅有尺寸的约束,还有装饰(颜色、边框、等)、内外边距等需求的情况。
ConstrainedBox
ConstrainedBox组件约束子组件的最大宽高和最小宽高,假如一个组件宽高都是300,包裹在ConstrainedBox中,并给ConstrainedBox添加最大宽高约束,用法如下:
ConstrainedBox(
constraints: BoxConstraints(maxHeight: 60, maxWidth: 200),
child: Container(height: 300, width: 300, color: Colors.red),
)
这时子组件是无法突破BoxConstraints设置的最大宽高:
如果BoxConstraints嵌套使用,有2个ConstrainedBox
ConstrainedBox(
constraints: BoxConstraints(maxHeight: 60, maxWidth: 200),
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: 100, maxWidth: 240),
child: Container(height: 300, width: 300, color: Colors.red),
),
)
以最大宽为例,第一个BoxConstraints的maxHeight值是60,也就是约束其子控件最大高是60,第二个BoxConstraints的maxHeight值是100,由于第二个BoxConstraints也受第一个的约束,所以第二个BoxConstraints最大高也只能是60,最终子组件的最大高是60,同理最大宽是200,因此多级BoxConstraints嵌套约束最大值最终值等于多个BoxConstraints约束中的最小值。同理嵌套约束最小值等于多个BoxConstraints约束中的最大值
- UnconstrainedBox
区别是不做截取操作
因此在开发中会出现这个黄色越界边框
- SizedBox
SizedBox是具有固定宽高的组件,直接指定具体的宽高
SizedBox(
height: 60,
width: 200,
child: RaisedButton(
child: Text('this is SizedBox'),
),
)
我们也可以设置尺寸无限大
SizedBox(
height: double.infinity,
width: double.infinity,
...
)
SizedBox可以没有子组件,但仍然会占用空间,所以SizedBox非常适合控制2个组件之间的空隙
Column(
children: <Widget>[
Container(height: 30,),
SizedBox(height: 10,),
Container(height: 30,),
],
)
- AspectRatio
是固定宽高比的组件
如果组件的宽度固定,希望高是宽的1/2,可以用AspectRatio实现此效果:
AspectRatio(
aspectRatio: 2 / 1, // 可以直接写成分数的形式--可读性更高
child: Container(color: Colors.red),
)
- FractionallySizedBox
当我们需要一个控件的尺寸是相对尺寸时,比如当前按钮的宽度占父组件的70%,可以使用FractionallySizedBox来实现此效果。
使用FractionallySizedBox包裹子控件,设置widthFactor宽度系数或者heightFactor高度系数,系数值的范围是0-1,0.7表示占父组件的70%,用法如下:
FractionallySizedBox(
widthFactor: .7,
child: RaisedButton(
child: Text('button'),
),
)
通过alignment参数控制子组件显示的位置,默认为center:
FractionallySizedBox(
alignment: Alignment.centerLeft,
...
)
间隔父元素的百分之10
Column(
children: <Widget>[
Container(
height: 50,
color: Colors.red,
),
Flexible(
child: FractionallySizedBox(
heightFactor: .1,
),
),
Container(
height: 50,
color: Colors.blue,
),
],
)
- Container
Container组件应该是最常用的组件之一,Container组件可以直接设置其宽高
Container(
height: 100,
width: 100,
...
)
Container组件是这些组件里面属性最多的一个,当然也是用法最复杂的一个
- this.alignment 对齐方式
Container(
alignment: Alignment.bottomRight,
transform: Matrix4.rotationX(1),
child: Text('牛了啊'),
)
- this.padding Container和子元素之间添加空白可以使用padding属性
- this.color 背景颜色
- this.decoration decoration属性可以设置子控件的背景颜色、形状等
Container(
child: Text('牛了啊'),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.red
),
)
deoration和 color: 背景颜色不能共存,二者同时只能有一个
- this.foregroundDecoration 绘制在child前面的装饰
Container(
child: Text('牛了啊'),
foregroundDecoration: BoxDecoration(
shape: BoxShape.rectangle, color: Colors.indigo),
decoration:
BoxDecoration(shape: BoxShape.circle, color: Colors.red),
)
Container(
child: Text('牛了啊'),
foregroundDecoration: BoxDecoration(
shape: BoxShape.rectangle, color: Colors.transparent),
decoration:
BoxDecoration(shape: BoxShape.circle, color: Colors.red),
)
- double? width 宽度
- double? height 高度
- BoxConstraints? constraints,
- this.margin 外边距,用法与padding一样
- this.transform 通过transform可以旋转、平移、缩放Container
Container(
transform: Matrix4.rotationX(1),// 倾斜变换
child: Text('牛了啊'),
)
- this.transformAlignment,
旋转、平移、变化的对齐方式
上图不存在 transformAlignment
属性
Container(
transform: Matrix4.rotationX(1),
transformAlignment: Alignment.bottomRight,
child: Text('牛了啊'),
)
与alignment的区别的父容器的区别
-
this.child 子组件
-
this.clipBehavior 剪切行为--默认不剪切超出边界的内容
-
LimitedBox
LimitedBox组件是当不受父组件约束时,可以用LimitedBox
限制尺寸
如果LimitedBox的父组件受到约束,此时LimitedBox将会不做任何操作,我们可以认为没有这个组件,代码如下:
children:[
Container(
height: 100,
width: 100,
child: LimitedBox(
maxHeight: 50,
maxWidth: 100,
child: Container(
color: Colors.green,
),
),
)
]
LimitedBox设置的宽高不是正方形,此时效果时正方形,说明LimitedBox没有起作用。
在ListView中直接添加Container组件:
ListView(
children: <Widget>[
Container(
color: Colors.green,
),
Container(
color: Colors.red,
),
],
)
这时你会发现什么也没有,因为在容器不受约束时,大小将会设置0,只需将Container包裹在LimitedBox
(SizedBox
都可以--反正就是需要宽高,在没有约束的富容器中不能使用Container)中即可:
ListView(
children: <Widget>[
LimitedBox(
maxHeight: 100,
child: Container(
color: Colors.green,
),
),
LimitedBox(
maxHeight: 100,
child: Container(
color: Colors.red,
),
),
],
)
-
Clip (剪裁)
- ClipOval : 子组件为正方形时剪裁为内贴圆形,为矩形时,剪裁为内贴椭圆
SizedBox( height: 100, child: ClipOval( child: Container( color: Colors.green, )), //剪裁为圆形 )
- ClipRRect : 将子组件剪裁为圆角矩形
ClipRRect( borderRadius: BorderRadius.all(Radius.elliptical(30, 30)), child: Container( color: Colors.green, )) ClipRRect( //剪裁为圆角矩形 borderRadius: BorderRadius.circular(5.0), child: Container( color: Colors.green, ), )
- ClipRect : 剪裁子组件到实际占用的矩形大小(溢出部分剪裁)
Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ ClipRect( //将溢出部分剪裁 child: Align( alignment: Alignment.topLeft, widthFactor: .5, //宽度设为原来宽度一半 child: Text("冲冲冲"), ), ), Text("你好世界", style: TextStyle(color: Colors.green)) ], )
- CustomClipper
剪裁子组件的特定区域
class MyClipper extends CustomClipper<Rect> { @override Rect getClip(Size size) => Rect.fromLTWH(10.0, 15.0, 40.0, 30.0); @override bool shouldReclip(CustomClipper<Rect> oldClipper) => false; } ClipRect( clipper: MyClipper(), //使用自定义的clipper child: avatar )