最近打算自己做一个电商 App 调研之后选择技术栈为 Flutter 因此打算梳理一下 Flutter 的所有知识点,做个预热。
- 对于 Column 组件,我们设置 mainAxisSize: MainAxisSize.min 之后,其中的内容就自动居中了。原理很有可能是原来是沾满整个高度方向并且居中的,现在设置成为内容撑开之后虽然高度变化了,但是默认居中还在,于是就自动跑到屏幕的中间了。
在 Flutter 框架中,Column组件是一个线性布局的组件,它将子组件沿着主轴(默认是垂直方向)排列。mainAxisSize属性决定了Column的主轴尺寸行为。
MainAxisSize.min和MainAxisSize.max是MainAxisSize的两个可能值:
-
MainAxisSize.max:这是默认值。当设置为max时,Column会尝试占据尽可能多的空间,即它会扩展以填充其父组件分配给它的空间。在这种情况下,如果Column中的子组件没有指定高度,Column会扩展以填满整个可用空间,使得子组件默认情况下不会居中,而是会堆叠在一起。 -
MainAxisSize.min:当设置为min时,Column会根据其子组件的实际大小来确定自己的大小,而不是尝试占据所有可用空间。这意味着Column会收缩到其子组件所需的最小尺寸。如果子组件没有填满整个空间,Column会保持在子组件的大小,而不会扩展。
当你将Column的mainAxisSize设置为MainAxisSize.min时,Column的高度将由其子组件的最小尺寸决定。如果Column的子组件没有填满整个可用空间,并且Column内部没有额外的对齐或间距设置,那么Column中的子组件将会保持它们在主轴上的默认对齐方式,这通常是居中对齐。因此,如果Column的子组件没有指定特定的对齐方式,它们将会在Column的中心垂直堆叠。
总结来说,当你设置mainAxisSize: MainAxisSize.min,你实际上是在告诉Column不要扩展以填满所有可用空间,而是根据其子组件的实际大小来确定自己的大小。如果子组件没有填满整个空间,它们将在Column中居中对齐,因为这是Column在没有额外对齐设置时的默认行为。
- 我们通过如下的方式增加一个按钮组件并通过 TextButton.styleFrom 设置其样式:
TextButton(
onPressed: rollDice, // 确保rollDice是一个定义好的函数
style: TextButton.styleFrom(
padding: const EdgeInsets.only(top: 20), // 设置按钮的内边距
foregroundColor: Colors.white, // 设置按钮的前景色,这里假设是文字颜色
textStyle: const TextStyle(
fontSize: 28, // 设置文字样式的字体大小
),
child: const Text('Roll Dice'), // 设置按钮显示的文本
),
)
- 为了在高度方向上留出空隙,除了给下面的元素增加 padding 之外(如上述代码所示),我们可以利用 SizedBox 组件进行占位:
const SizeBox(height: 20), - 响应式组件基础 为了实现组件随着某个事件的发生(这通常可能是按钮被点击)而做出改变,我们必须使用带有状态的 widget 而不是无状态的组件;实现响应式的一个基本思路,非常类似于 Echarts 的刷新,那就是当回调函数被执行之后,调用组件的重载方法(setState)并将新的状态传递给此重载方法,注意这个和 Vue 不同,组件的刷新不是理所应该或者自发的,而是需要手动调用重载的方法,类似于 React.
- Flutter 的调试代码是
print(123)等同于console.log(123) - 在 Dart 中以下划线开头的 identifor 是私有的,类名等。
- StatefulWidget 类是实现响应式组件的基础:
import 'package:flutter.material.dart';
class DiceRoller extends StatefulWidget {
const DiceRoller({super.key});
@override
State<DiceRoller> createState(){
return _DiceRollerState();
}
}
class _DiceRollerState extends State<DiceRoller> {
var color = 1;
void changeColor(){
setState((){
color = 2;
})
}
@override
Widget build(context) {
return Column(...)
}
}
上面的代码就是创建一个可变组件的基本过程。对于上面的代码,有一下几点需要说明:
- 在 class 作用域之内,我们可以通过 var 直接创建变量,可以直接通过 void 创建函数,这与 js 语法有着相当大的不同;
- dart 中的匿名函数形如
(){}这个和 js 也是不同的()=>{}; - setState 是一个函数,接受一个形参函数,是内置对象,无需再引入。
- Dart 中产生随机数:
Random.nextInt(10)表示产生一个0-9的随机数,不难看出是左闭右开的;使用 Random 需要引入一个库,名为:import 'dart:math'; - Dart 中的 template 类似于 js 但是不需要加花括号,这样看来更像 shell 语法:
'assets/images/dice-$diceRoll.png';但不使用反引号。 - 一个点击按钮切换图片的可变组件示例:
class _DiceRollerState extends State<DiceRoller> {
int currentDiceRoll = 2; // 初始化骰子的当前点数为2
// 掷骰子的方法
void rollDice() {
setState(() {
currentDiceRoll = Random().nextInt(6) + 1; // 生成1到6的随机数
});
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Image.asset(
'assets/images/dice-$currentDiceRoll.png',
width: 200,
),
// 这里似乎有代码错误,我将假设你想要添加一个按钮来掷骰子
ElevatedButton(
onPressed: rollDice, // 当按钮被按下时,调用rollDice方法
child: Text('Roll Dice'),
),
],
);
}
}
可以针对 Random 部分进行优化:
final randomizer = Random();
int currentDiceRoll = 2; // 初始化骰子的当前点数为2
// 掷骰子的方法
void rollDice() {
setState(() {
currentDiceRoll = randomizer.nextInt(6) + 1; // 生成1到6的随机数
});
}
这样一来我们就减少了 Random 的实例化次数,因为在临时变量前面加上了名为 final 的修饰符。
-
Stateless Widget versus Stateful Widget
Stateless Widgets Stateful Widgets 不管理任何内部数据 管理内部数据(“状态”) 仅在父组件更新(“重新渲染”)时更新屏幕 当状态变化时,组件被重新渲染,并且 UI 更新 应作为默认选择:尽可能多地使用 每当有变化的数据需要引起 UI 更新时使用 -
dart 文件的连接符号应该是下划线而不是小驼峰或者大驼峰格式:
start_screen.dart -
Center 组件只是将其子组件在水平和垂直方向上居中对齐,并不会影响子组件的大小。如果子组件的大小不足以填满屏幕,那么 Center 会将子组件居中显示,周围会有空白。
@override
Widget build(context) {
return const Center(child: Text('Start Screen'));
}
- 一个上图下文组件示例
children: [
Image.asset(
'assets/images/quiz-logo.png',
width: 300,
),
const SizedBox(height: 80),
const Text(
'Learn Flutter the fun way!',
style: TextStyle(
color: Colors.white,
),
),
]
- 配置一个 outlined button
OutlinedButton(
onPressed: () {
// 在这里添加按钮点击时的逻辑
},
style: OutlinedButton.styleFrom(
foregroundColor: Colors.white, // 设置按钮的前景色为白色
),
child: const Text('Start Quiz'), // 设置按钮上的文本
)
我们还可以使用其另外一个构造函数,方便的写出带图标的按钮:
OutlinedButton.icon(
onPressed: () {
// 在这里添加按钮点击时的逻辑
},
style: OutlinedButton.styleFrom(
foregroundColor: Colors.white, // 设置按钮的前景色为白色
),
icon: const Icon(Icons.arrow_right_alt),
label: const Text('Start Quiz'), // 设置按钮上的文本
)
- 两种设置透明度的方法 一种是使用 Opacity 组件将对象组件进行包裹,这个外包组件会修改被包裹的组件整体的透明度,开销比较大,也不是很灵活:
Opacity(
opacity: 0.6,
child: Image.asset(
'assets/images/quiz-logo.png',
width: 300,
),
)
另外一种就是使用具有透明度的颜色,如 ARGB 中的 A 表示的就是透明度,或者填充度,为 1 时颜色最饱和。
Image.asset(
'assets/images/quiz-logo.png',
width: 300,
color: const Color.fromARGB(150, 255, 255, 255),
)
这个方法当然可以用来创建具有透明度的任何颜色,比如字体颜色。
在 Flutter 中,Image 组件的 color 属性可以用来对图片进行着色。通过设置 color 属性,你可以改变图片的颜色,而不需要修改原始图片文件。