flutter学习

Anrola Lv1

零.门

flutter文档:https://docs.flutter.cn/

安装 Git

安装 Visual Studio Code

向 VS Code 添加 DartFlutter 扩展。

在命令面板选择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(无状态窗口)

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打交道。

三.几种基础的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';

/// Flutter code sample for [Switch].

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(
// This bool value toggles the switch.
value: light,
activeThumbColor: Colors.red,
onChanged: (bool value) {
// This is called when the user toggles the switch.
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';

/// Flutter code sample for [Slider].
/// set to false.

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(
// ignore: deprecated_member_use
year2023: year2023,
value: _currentSliderValue,
max: 100,
onChanged: (double value) {
setState(() {
_currentSliderValue = value;
});
},
),
Slider(
// ignore: deprecated_member_use
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"), //设置文字
),

ElevatedButton

升降按钮,按下时会有升降效果。
参数与TextButton类似。

IconButton

图标按钮。

1
2
3
4
5
6
IconButton(
onHover: (value) {},
onLongPress: () {},
onPressed: () {},
icon: Icon(Icons.close),
),

SegmentedButton

一排选择性按钮,一般用于切换页面。

必须注意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(() {
// By default there is only a single segment that can be
// selected at one time, so its value is always the first
// item in the selected set.
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: () {},
),
//默认fixed从底部滑入,floating则为浮动。
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

//也可以自己设置xy轴
AlignmentGeometry.xy(0,0) //等价与居中

AlignmentGeometry.xy(-1,-1) //左上角

AlignmentGeometry.xy(-1,1) //左下角

AlignmentGeometry.xy(-0.5,-0.5) // 左上角和中心点的中间

key

分为LocalKeyGlobalKey
理解为窗口的身份证,GlobalKey是这个窗口在整个软件里唯一的身份证,LocalKey是这个窗口的父元素里唯一的身份证。
抽象比喻为,GlobalKey是国家发的身份证,LocalKey是某家公司内部用的工号。

通过key能获取到窗口的Context、State和Widget。
在多窗口联动时可能会有用。

padding 和 margin

padding内边距。
margin外边距。
两者属性差不多,以padding为例说明。

1
EdgeInsets.all(8)

所有边都有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();
}
}


// 注意这里的 with SingleTickerProviderStateMixin
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(() {
//value监听,每次value改变都会调用这里。
//在这里加上setState,就能持续改变页面的各种属性。
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
//alpha为透明度,red、green、blue分别为红绿蓝
//取值0~255
color: Color.fromARGB(alpha, red, green, blue)

//可以指定颜色空间
Color.from(alpha: alpha, red: red, green: green, blue: blue, colorSpace: ColorSpace.sRGB)

//rgb取值0~255,opacity透明的取值0.0~1.0
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, // 混合Mode
this.shape = BoxShape.rectangle, // 形状
})

//例子:
BoxDecoration(
border: new Border.all(color: Colors.red, width: 0.5), // 边色与边宽度
color: Colors.blue, // 底色
borderRadius: new BorderRadius.circular((20.0)), // 圆角度


//生成两层阴影,一层绿,一层黄
//阴影位置由offset决定
//阴影模糊程度由blurRadius决定
//阴影模糊大小由spreadRadius决定
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
)
//扫描式渐变
//gradient: SweepGradient(colors: [Color(0xFFFFFF00), Color(0xFF00FF00), Color(0xFF00FFFF)], startAngle: 0.0, endAngle: 1*3.14)

//线性渐变
//gradient: LinearGradient(colors: [Color(0xFFFFFF00), Color(0xFF00FF00), Color(0xFF00FFFF)], begin: FractionalOffset(1, 0), end: FractionalOffset(0, 1))
)

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), // Colors.blue[400]
this.darkColor = const Color(0xFF0D47A1), // Colors.blue[900]
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); // 宽度固定200,高度无约束
BoxConstraints.tightFor(height: 100); // 高度固定100,宽度无约束

//宽松约束
//子Widget可以在0~size范围内自由取值,默认自适应内容
BoxConstraints.loose(const Size(200, 100));

//充满约束
//子Widget会充满父节点给的最大可用空间
BoxConstraints.expand(); // 充满父节点所有可用空间
BoxConstraints.expand(width: 300); // 宽度充满,高度固定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(),
],
),
);
  • Title: flutter学习
  • Author: Anrola
  • Created at : 2026-08-08 15:12:32
  • Updated at : 2026-08-13 18:12:31
  • Link: https://redefine.ohevan.com/2026/08/08/flutter-learn-1/
  • License: This work is licensed under CC BY-NC-SA 4.0.