零.门 flutter文档:https://docs.flutter.cn/
安装 Git 。
安装 Visual Studio Code 。
向 VS Code 添加 Dart 和 Flutter 扩展。
在命令面板选择Flutter: New Project 。 VS Code 会提示你在计算机上定位 Flutter SDK。选择 Download SDK 。
一.你好世界 在命令面板选择Flutter: New Project 创建flutter项目。 此时就可以尝试运行项目了。 所有的代码都应该在lib 里面书写。 此外,如果需要Window和Android版,则需要安装VS Studio和Android Studio。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import 'package:flutter/material.dart' ;void main() { runApp(const MainApp()); } class MainApp extends StatelessWidget { const MainApp({super .key}); @override Widget build(BuildContext context) { return const MaterialApp( home: Scaffold( body: Center( child: Text('Hello World!' ), ), ), ); } }
可以尝试初步理解为“坑位 ” 和“窗口 ”。
以上面为例子,MainApp继承StatelessWidget(无状态窗口),通过build函数构建窗口。
build函数返回一个MaterialApp类型的窗口 ,
MaterialApp里面有坑位home (MaterialApp的主体)。 在这个坑位home 里放入了一个窗口Scaffold (框架)。
窗口Scaffold 里又分了很多坑位 ,在坑位body 里填入一个窗口Center ,在窗口Center 里的所有坑位都会居中显示。
窗口Center 里又有坑位child ,在坑位child 里填入窗口Text ,窗口Text 用于显示文字,窗口Text 也有文字的坑位 ,填入字符串’Hello World!’。
这样坑位 与窗口 的无限套娃,就构成了这个简单的界面。
其实这样看非常像HTML,如果做过网页开发,很快就能上手。
StatelessWidget的创建非常简单。
1 2 3 4 5 6 7 8 9 10 import 'package:flutter/material.dart' ;class MainBackGround extends StatelessWidget { const MainBackGround({super .key}); @override Widget build(BuildContext context) { return Container(color:Colors.red); } }
这样就获得了一个纯色的窗口了。 最核心的build函数,负责构建页面上的各种元素。 他需要返回一个Widget类,而Widget又是flutter最核心的类。 在实际写页面时,有很多时间都在和Widget打交道。
Container 基础容器。万能套娃Widget。 常用的属性基本都有。
1 2 3 4 5 6 Container( margin: const EdgeInsets.all(10.0 ), color: Colors.amber[600 ], width: 48.0 , height: 48.0 , )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Container( constraints: BoxConstraints.expand( height: Theme.of(context).textTheme.headlineMedium!.fontSize! * 1.1 + 200.0 , ), padding: const EdgeInsets.all(8.0 ), color: Colors.blue[600 ], alignment: Alignment.center, transform: Matrix4.rotationZ(0.1 ), child: Text('Hello World' , style: Theme.of(context) .textTheme .headlineMedium! .copyWith(color: Colors.white)), )
Column 列布局。
1 2 3 4 5 6 7 8 9 Column( children: <Widget>[ Text('hello' ), Text('world' ), ], spacing:10 , )
核心的children参数运行放入多个Widget,多个Widget会排成列。 可以用spacing设置间隔。
Row 行布局,与Colum类似。
1 2 3 4 5 6 7 8 9 Row( children: <Widget>[ Text('hello' ), Text('world' ), ], spacing:10 , )
Scaffold 一种布局,方便制造各种常用功能。 本身包含了常用的属性,可以很方便的设置:appBar :顶部条栏body :主体部分floatingActionButton :浮动按钮,默认在右下角floatingActionButtonLocation :浮动按钮的位置bottomNavigationBar :底部导航栏
使用时一般放在Material内。
1 2 3 4 5 6 7 8 9 10 11 MaterialApp( home: Scaffold( backgroundColor: Colors.amber, body: Center( child: TextButton( onPressed: () {}, child: const Text('111' ), ), ), ), );
TextField 输入框。
1 2 3 4 5 6 7 TextField( obscureText: true , decoration: InputDecoration( border: OutlineInputBorder(), labelText: 'Password' , ), )
Checkbox 勾选框。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import 'package:flutter/material.dart' ;void main() => runApp(const CheckboxExample());class CheckboxExample extends StatefulWidget { const CheckboxExample({super .key}); @override State<CheckboxExample> createState() => _CheckboxExampleState(); } class _CheckboxExampleState extends State <CheckboxExample > { bool isChecked = false ; @override Widget build(BuildContext context) { Color getColor(Set <WidgetState> states) { const Set <WidgetState> interactiveStates = <WidgetState>{ WidgetState.pressed, WidgetState.hovered, WidgetState.focused, }; if (states.any(interactiveStates.contains)) { return Colors.blue; } return Colors.red; } return Material( child: Checkbox( checkColor: Colors.white, fillColor: WidgetStateProperty.resolveWith(getColor), value: isChecked, onChanged: (bool? value) { setState(() { isChecked = value!; }); }, ), ); } }
Switch 开关。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import 'package:flutter/material.dart' ;void main() => runApp(const SwitchApp());class SwitchApp extends StatelessWidget { const SwitchApp({super .key}); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('Switch Sample' )), body: const Center(child: SwitchExample()), ), ); } } class SwitchExample extends StatefulWidget { const SwitchExample({super .key}); @override State<SwitchExample> createState() => _SwitchExampleState(); } class _SwitchExampleState extends State <SwitchExample > { bool light = true ; @override Widget build(BuildContext context) { return Switch( value: light, activeThumbColor: Colors.red, onChanged: (bool value) { setState(() { light = value; }); }, ); } }
Slider 滑动条。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 import 'package:flutter/material.dart' ;void main() => runApp(const SliderExampleApp());class SliderExampleApp extends StatelessWidget { const SliderExampleApp({super .key}); @override Widget build(BuildContext context) { return const MaterialApp(home: SliderExample()); } } class SliderExample extends StatefulWidget { const SliderExample({super .key}); @override State<SliderExample> createState() => _SliderExampleState(); } class _SliderExampleState extends State <SliderExample > { double _currentSliderValue = 20 ; double _currentDiscreteSliderValue = 60 ; bool year2023 = true ; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Slider' )), body: Center( child: Column( mainAxisAlignment: .center, spacing: 16 , children: <Widget>[ Slider( year2023: year2023, value: _currentSliderValue, max: 100 , onChanged: (double value) { setState(() { _currentSliderValue = value; }); }, ), Slider( year2023: year2023, value: _currentDiscreteSliderValue, max: 100 , divisions: 5 , label: _currentDiscreteSliderValue.round().toString(), onChanged: (double value) { setState(() { _currentDiscreteSliderValue = value; }); }, ), SwitchListTile( value: year2023, title: year2023 ? const Text('Switch to latest M3 style' ) : const Text('Switch to year2023 M3 style' ), onChanged: (bool value) { setState(() { year2023 = !year2023; }); }, ), ], ), ), ); } }
TextButton 顾名思义文字按钮。
1 2 3 4 5 6 7 8 9 10 11 TextButton( onFocusChange: (value) {}, onHover: (value) {}, onLongPress: () {}, onPressed: () {}, style: ButtonStyle( backgroundColor: WidgetStatePropertyAll(Colors.red) ), child: Text("1111" ), ),
升降按钮,按下时会有升降效果。 参数与TextButton类似。
图标按钮。
1 2 3 4 5 6 IconButton( onHover: (value) {}, onLongPress: () {}, onPressed: () {}, icon: Icon(Icons.close), ),
一排选择性按钮,一般用于切换页面。
必须注意selected和onSelectionChanged。 segments里的各个value都要和selected里的对上。
官方示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 class SegmentedButtonExample extends StatefulWidget { const SegmentedButtonExample({super .key}); @override State<SegmentedButtonExample> createState() => _SegmentedButtonExampleState(); } enum Calendar { day, week, month, year }class _SegmentedButtonExampleState extends State <SegmentedButtonExample > { Calendar calendarView = .week; @override Widget build(BuildContext context) { return SegmentedButton<Calendar>( style: SegmentedButton.styleFrom( backgroundColor: Colors.grey[200 ], foregroundColor: Colors.red, selectedForegroundColor: Colors.white, selectedBackgroundColor: Colors.green, ), segments: const <ButtonSegment<Calendar>>[ ButtonSegment<Calendar>( value: Calendar.day, label: Text('Day' ), icon: Icon(Icons.calendar_view_day), ), ButtonSegment<Calendar>( value: Calendar.week, label: Text('Week' ), icon: Icon(Icons.calendar_view_week), ), ButtonSegment<Calendar>( value: Calendar.month, label: Text('Month' ), icon: Icon(Icons.calendar_view_month), ), ButtonSegment<Calendar>( value: Calendar.year, label: Text('Year' ), icon: Icon(Icons.calendar_today), ), ], selected: <Calendar>{calendarView}, onSelectionChanged: (Set <Calendar> newSelection) { setState(() { calendarView = newSelection.first; }); }, ); } }
Badge 本质是一种Icon。 可以在Icon上设置标签和计数。 配合IconButton使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 IconButton( icon: const Badge( label: Text('Your label' ), backgroundColor: Colors.blueAccent, child: Icon(Icons.receipt), ), onPressed: () {}, ) IconButton( icon: Badge.count( count: 9999 , child: const Icon(Icons.notifications), ), onPressed: () {}, )
SnackBar 从底部弹出提示,特别的是提示上可以附带操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text('Awesome Snackbar!' ), action: SnackBarAction( label: 'Action' , onPressed: () {}, ), behavior: SnackBarBehavior.floating, duration: Duration (milliseconds: 2000 ), ), );
续一.各种属性 接下来先以Container内可以填入的属性为例子介绍各种属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Key? key, AlignmentGeometry? alignment, EdgeInsetsGeometry? padding, Color? color, bool isAntiAlias = true ,Decoration? decoration, Decoration? foregroundDecoration, double? width,double? height,BoxConstraints? constraints, EdgeInsetsGeometry? margin, Matrix4? transform, AlignmentGeometry? transformAlignment, Widget? child, Clip clipBehavior = Clip.none,
alignment alignment用于设置位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 AlignmentGeometry.center AlignmentGeometry.bottomCenter AlignmentGeometry.topCenter AlignmentGeometry.centerLeft AlignmentGeometry.xy(0 ,0 ) AlignmentGeometry.xy(-1 ,-1 ) AlignmentGeometry.xy(-1 ,1 ) AlignmentGeometry.xy(-0.5 ,-0.5 )
key 分为LocalKey 和GlobalKey 。 理解为窗口的身份证,GlobalKey是这个窗口在整个软件里唯一的身份证,LocalKey是这个窗口的父元素里唯一的身份证。 抽象比喻为,GlobalKey是国家发的身份证,LocalKey是某家公司内部用的工号。
通过key能获取到窗口的Context、State和Widget。 在多窗口联动时可能会有用。
padding 和 margin padding内边距。 margin外边距。 两者属性差不多,以padding为例说明。
所有边都有8的内边距。
1 EdgeInsets.only(left: 8 ,right: 8 ,top: 8 ,bottom: 8 )
单独指定各边的内边距。
1 EdgeInsets.symmetric(vertical: 10 ,horizontal: 10 )
对称型内边距,可以设置垂直和水平的内边距。
1 EdgeInsets.lerp(EdgeInsets.all(0 ), EdgeInsets.all(10 ),0 )
线性插值,表示从0到10的插值。 t表示时间线上的位置,0时为EdgeInsets.all(0),1时为EdgeInsets.all(10)。 即便超过0或1也依然有效果。
通常搭配AnimationController使用。下面一个简单的例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 import 'package:flutter/material.dart' ;class W2 extends StatefulWidget { const W2({super .key}); @override State<StatefulWidget> createState() { return W2State(); } } class W2State extends State <W2 > with SingleTickerProviderStateMixin { late AnimationController animationController; double myPadding = 0 ; @override void initState() { super .initState(); animationController = AnimationController(vsync: this , duration: Duration (milliseconds: 10000 )) ..addStatusListener((AnimationStatus status) { if (status == AnimationStatus.completed) { animationController.reverse(); } else if (status == AnimationStatus.dismissed) { animationController.forward(); } }) ..addListener(() { setState(() { myPadding = animationController.value; }); }); animationController.forward(); } @override Widget build(BuildContext context) { return Container( padding: EdgeInsets.lerp( EdgeInsets.all(1 ), EdgeInsets.all(50 ), myPadding, ), color: Colors.red, child: Container(color: Colors.amber), ); } }
color 顾名思义颜色。 flutter预制了一些颜色:
1 2 3 4 Colors.red Colors.blue Colors.blue ....
也能从ARGB获取颜色
1 2 3 4 5 6 7 8 9 color: Color.fromARGB(alpha, red, green, blue) Color.from(alpha: alpha, red: red, green: green, blue: blue, colorSpace: ColorSpace.sRGB) Color.fromRGBO(r, g, b, opacity)
isAntiAlias 抗锯齿开关。默认ture。
decoration 和 foregroundDecoration 一个背景装饰对象,定制各种各样的背景(边框、圆角、阴影、形状、渐变、背景图像)。 其中decoration是背景,foregroundDecoration是前景。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 BoxDecoration({ this .color, this .image, this .border, this .borderRadius, this .boxShadow, this .gradient, this .backgroundBlendMode, this .shape = BoxShape.rectangle, }) BoxDecoration( border: new Border.all(color: Colors.red, width: 0.5 ), color: Colors.blue, borderRadius: new BorderRadius.circular((20.0 )), boxShadow: [BoxShadow(color: Color(0x99FFFF00 ), offset: Offset(5.0 , 5.0 ),blurRadius: 10.0 , spreadRadius: 2.0 ), BoxShadow(color: Color(0x9900FF00 ), offset: Offset(1.0 , 1.0 )), BoxShadow(color: Color(0xFF0000FF ))], gradient: RadialGradient( colors: [ Color(0xFFFFFF00 ), Color(0xFF00FF00 ), Color(0xFF00FFFF ) ], radius: 1 , tileMode: TileMode.mirror ) )
1 2 3 4 5 6 7 ShapeDecoration({ this .color, this .image, this .gradient, this .shadows, @required this .shape, })
ShapeDecoration只有shape属性不一样,单独研究一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 shape: Border.all(color: Color(0xFF00FFFF ),style: BorderStyle.solid,width: 2 ) shape: Border(top: b, bottom: b, right: b, left: b) shape: UnderlineInputBorder(borderSide:BorderSide(color: Color(0xFFFFFFFF ), style: BorderStyle.solid, width: 2 )) shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10 )), side: BorderSide(color: Color(0xFFFFFFFF ), style: BorderStyle.solid, width: 2 )) shape: CircleBorder(side: BorderSide(color: Color(0xFFFFFF00 ), style: BorderStyle.solid, width: 2 )) shape: StadiumBorder(side: BorderSide(width: 2 , style: BorderStyle.solid, color: Color(0xFF00FFFF )) shape: BeveledRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10 )), side: BorderSide(color: Color(0xFFFFFFFF ), style: BorderStyle.solid, width: 2 ))
1 2 3 4 5 6 7 FlutterLogoDecoration({ this .lightColor = const Color(0xFF42A5F5 ), this .darkColor = const Color(0xFF0D47A1 ), this .textColor = const Color(0xFF616161 ), this .style = FlutterLogoStyle.markOnly, this .margin = EdgeInsets.zero, })
专门用作logo的Decoration。
1 2 3 4 UnderlineTabIndicator({ this .borderSide = const BorderSide(width: 2.0 , color: Colors.white), this .insets = EdgeInsets.zero, })
用于添加下划线指示。
width 和 height 指定高度和宽度
constraints 尺寸约束,划定一个“可活动的尺寸范围”,实际尺寸必须在这个范围内取值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 BoxConstraints({ this .minWidth = 0.0 , this .maxWidth = double .infinity, this .minHeight = 0.0 , this .maxHeight = double .infinity, }); BoxConstraints.tight(Size(200 , 100 )); BoxConstraints.tightFor(width: 200 ); BoxConstraints.tightFor(height: 100 ); BoxConstraints.loose(const Size(200 , 100 )); BoxConstraints.expand(); BoxConstraints.expand(width: 300 );
child 实现套娃的重要参数。 里面可以放任何Widget,几乎是最最常用的参数。
1 2 3 4 5 6 Container( padding: EdgeInsets.symmetric(vertical: 50 ), color: Colors.red, child: Container(color: Colors.blue), );
续二.孩子 在一般的组件里只能套一个孩子。
1 2 3 Container( child: Container(), );
为了能实现多个孩子嵌套,则需要Row和Column的帮助。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 Container( child: Row( children: [ Container(), Container(), Container() ] ), ); Container( child: Row( children: [ Container(), Column( children: [ Container(), Container(), Container() ] ), Container(), ], ), );