Flutter 学习
Dart 笔记地址:Dart
配置 Flutter 环境地址:选择你的开发平台,开始使用
创建 & 运行
创建项目
创建 Flutter 有两种方式:
使用 VSCode 控制台,键盘上输入快捷键 Ctrl + Shift + P 或者 Command + Shift + P
输入 Flutter: New Project,输入项目名即可
控制台输入:
flutter create <project-name>即可创建
运行项目:
使用 flutter run运行项目
调试:
在控制台直接输入:
r:重新载入,加载更新p:显示网格,用于查看布局o:切换android和ios的预览模式。q:退出预览模式
目录结构

其中最重要的是如下文件夹,其他不必理会:
入口文件
在 Flutter/lib/main.dart 文件就是整个项目的入口文件。
main.dart 中的
void main() {
runApp(MyApp())
}
// 也可以简写
void main() => runApp(MyApp())组件 Widget
Widget 概述
Flutter 中,一切皆是组件,每个组件都是一个类。
Widget 是 Flutter 应用用户界面的构建块。
Widget 根据组合形成层次结构,每个 widget 都嵌套在其父级中,并且可以从父级接收上下文。
Widget 其实就是一个构成用户界面的基本单位
有布局小组件,比如
Padding, Alignment, Row, Column, Grid等,这类布局组件没有其视觉表达,其唯一的目的就是控制内部小组件的布局方面。还有一系列实用组件,这些组件有可视化表达,比如:
Text, Icon, Image等每一个 Widget 都有一个 build 方法,我们需要覆盖 Widget 方法,并且让其返回一个 Widget
Widgets
关于 Flutter,你经常会听到 “一切都是一个小部件”。Widget 是 Flutter 应用用户界面的构建块,每个 widget 都是用户界面部分的不可变声明。
Widget 根据组合形成层次结构。每个 widget 都嵌套在其父级中,并且可以从父级接收上下文。此结构一直延续到根 widget,如这个简单的示例所示:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp( // Root widget
home: Scaffold(
appBar: AppBar(
title: const Text('My Home Page'),
),
body: Center(
child: Builder(
builder: (context) {
return Column(
children: [
const Text('Hello, World!'),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
print('Click!');
},
child: const Text('A button'),
),
],
);
},
),
),
),
);
}
}Center 组件
import 'package:flutter/material.dart';
void main() {
runApp(const Center(
child: Text(
'Hello Flutter',
textDirection: TextDirection.ltr,
),
));
}
Row 水平布局 / Column 垂直布局
使用 Row 组件进行水平排列, 使用 Column 进行垂直布局。
分为主轴和交叉轴,主轴使用 mainAxisAlignment 控制,交叉轴使用 crossAxisAlignment 控制。
布局属性:
mainAxisAlignment:MainAxisAlignment.spaceEvenly类似 Flex 布局中的主轴对齐方式crossAxisAlignment:CrossAxisAlignment.center类似 Flex 布局中的交叉轴对齐方式
Row:
Widget build(BuildContext context) {
return Row(
children: [
BorderedImage(),
BorderedImage(),
BorderedImage(),
],
);
}
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
BorderedImage(),
BorderedImage(),
BorderedImage(),
],
);
}
Column:
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
BorderedImage(),
Text('Dash 1'),
],
),
Column(
children: [
BorderedImage(),
Text('Dash 2'),
],
),
Column(
children: [
BorderedImage(),
Text('Dash 3'),
],
),
],
);
}
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
BorderedImage(),
BorderedImage(),
BorderedImage(),
]
),
}
Expanded 类 Flex 布局
当布局太大而无法容纳设备时,受影响的边缘会出现黄色和黑色的条纹图案。在此示例中,视区宽 400 像素,每个子对象宽 150 像素。
可以使用 Expanded Widget 调整 Widget 的大小以适合行或列。要修复前面的示例,即图像行对于其渲染框来说太宽,请使用 Expanded 小部件包装每个图像。
Widget build(BuildContext context) {
return const Row(
children: [
Expanded(
child: BorderedImage(width: 150, height: 150),
),
Expanded(
child: BorderedImage(width: 150, height: 150),
),
Expanded(
child: BorderedImage(width: 150, height: 150),
),
],
);
}
Expanded widget 还可以指示 widget 相对于其同级应占用多少空间。例如,您可能希望一个 Widget 占用的空间是其同级的两倍。为此,请使用 Expanded widgets flex 属性,这是一个确定 widget 的 flex 因子的整数。默认弯曲系数为 1。以下代码将中间图像的弯曲系数设置为 2:
🤯 其实就是类似于 Flex 布局,可以控制换这个子项要占用几份
Widget build(BuildContext context) {
return const Row(
children: [
Expanded(
child: BorderedImage(width: 150, height: 150),
),
Expanded(
flex: 2,
child: BorderedImage(width: 150, height: 150),
),
Expanded(
child: BorderedImage(width: 150, height: 150),
),
],
);
}
MaterialApp 结构组件
MaterialApp 是 Flutter 框架中一个非常重要的组件,它为应用提供了一个遵循 Material Design 规范的基本结构和主题。MaterialApp 通常是 Flutter 应用程序的根部件,负责管理路由、主题、语言环境等。
🌟 什么是 Material Design 规范?
Material Design 是 Google 设计的, 一个用于 App 设计的全面规范。它主要面向移动应用程序(尤其是 Android 应用),但也适用于 Web 和其他平台。这个设计语言旨在提供一致的用户体验,并通过一套统一的视觉、运动和互动原则来指导设计师和开发者。
Material Design 规范
材料和表面
材料的隐喻:Material Design 使用的“材料”是一种称为“纸张”的数字化材质,它具有物理世界中的属性,如厚度、边缘和阴影。
阴影和深度:通过阴影来表达界面的层次结构和交互性。布局
栅格系统:使用灵活的栅格系统进行布局设计,以确保应用在不同设备上的一致性。
响应式设计:内容和控件能够根据屏幕大小和方向自动调整。色彩
调色板:提供丰富多样的颜色选项,并包含主色、强调色和文本颜色。
色彩对比:确保足够的对比度,提高可读性和可访问性。排版
字体:使用清晰易读的排版,推荐使用 Google 的 Roboto 字体。
层次结构:通过不同的字体大小、字重和颜色来创建视觉层次。图标
一致性:图标应简单、直观并具有一致性。
可扩展性:设计成在不同尺寸和分辨率下都能很好地呈现。动画
自然动效:动画应模仿现实世界的物理效果,如加速、减速等。
意义明确:每个动画都应该有助于增强用户体验,而不是分散注意力。组件
卡片:用于显示相关信息和操作。
按钮:主按钮、副按钮及浮动按钮用于执行操作。
输入框:提供用户输入的方式,包括文本框、选择器等。模式
导航:通过抽屉菜单、标签页、底部导航栏等实现有效导航。
反馈:及时向用户提供操作结果的反馈信息。
Material Design 强调统一性、视觉美感和用户体验,使得应用程序在各个平台上都能提供一致的用户体验。更多详细信息可以参考 Material Design。
主要属性:
home: 指定应用的首页小部件。当应用启动时,这个小部件会被显示。通常是一个 Scaffold。
home: MyHomePage(),routes: 定义应用的路由表,是一个 Map<String, WidgetBuilder>,用于将字符串路径映射到不同的小部件构建器。
Scaffold 框架组件
Scaffold 是 Flutter 中一个常用的布局组件,它为 Material Design 应用提供了基本的视觉布局结构。Scaffold 可以帮助你快速搭建应用的基本视觉框架,比如提供导航栏、抽屉、底部导航栏等。
主要属性:
appBar: 用于设置顶部的应用栏(App Bar),通常包含标题、导航图标和操作按钮。由 AppBar 小部件实现。
appBar: AppBar(
title: Text('Home Page'),
),body: Scaffold 的主体部分,通常用于显示主要内容。你可以在这里放置各种类型的小部件,比如 Column, Row, ListView 等。
body: Center(
child: Text('Hello, World!'),
),drawer: 为应用添加一个左侧抽屉菜单,可以通过滑动或点击导航图标来打开。通常用于导航。
drawer: Drawer(
child: ListView(
children: <Widget>[
DrawerHeader(child: Text('Header')),
ListTile(title: Text('Item 1')),
ListTile(title: Text('Item 2')),
],
),
),bottomNavigationBar: 设置底部导航栏,典型应用是通过 BottomNavigationBar 实现分页导航。
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.settings), label: 'Settings'),
],
),floatingActionButton: 一个悬浮在 Scaffold 上的浮动按钮,通常用于实现一个最重要的用户动作,通过 FloatingActionButton 实现。
floatingActionButton: FloatingActionButton(
onPressed: () {
// Action to perform
},
child: Icon(Icons.add),
),bottomSheet: 用于显示固定在屏幕底部的面板。
bottomSheet: BottomSheet(
onClosing: () {},
builder: (context) => Container(
height: 100,
color: Colors.blue,
child: Center(child: Text('Bottom Sheet')),
),
),
Container 容器布局组件
Container 是一个方便的 widget,它可以负责布局、绘制、定位和调整大小。
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(16.0),
child: BorderedImage(),
);
}感觉有点像是 div
ListView 滚动组件
ListView是一个列状组件,当其内容长于渲染框时,会自动提供滚动,类似 React Native 中的 ScrollView。使用 ListView的最基本方法与使用 Column 和 Row 差不多。但不同的是,ListView 要求其子项占用交叉轴上的所有可用空间。
Widget build(BuildContext context) {
return ListView(
children: const [
BorderedImage(),
BorderedImage(),
BorderedImage(),
],
);
}
如果 child 有具体的内容,则使用上方方式,如果具有未知或非常大的数量的列表项时,最好使用 ListView.builder构造函数。
final List<ToDo> items = Repository.fetchTodos();
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, idx) {
var item = items[idx];
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(item.description),
Text(item.isComplete),
],
),
);
},
);
}
LayoutBuilder(Adaptive layouts 自适应布局)
我们可能使用 Flutter 构建移动、平板、桌面、web 程序,根据屏幕的不同大小做出不同行为。
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
if (constraints.maxWidth <= 600) {
return _MobileLayout();
} else {
return _DesktopLayout();
}
},
);
}
GestureDetector 点击事件组件
可以使用 GestureDetector 组件绑定 onTap 事件实现点击事件,无水波纹
GestureDetector(
onTap: () {
print(item);
Navigator.push(context, MaterialPageRoute(builder: (context) {
return WebViewPage(title: item['title']);
}));
},
child: ......
)InkWell 点击事件组件(水波纹)
可以使用 InkWell 组件绑定 onTap 事件实现点击事件,带水波纹
InkWell(
onTap: () {
print(item);
Navigator.push(context, MaterialPageRoute(builder: (context) {
return WebViewPage(title: item['title']);
}));
},
child: ......
)Navigator 路由组件
使用 Navigator 组件进行跳转,需要返回一个 Widget
Navigator.push(context, MaterialPageRoute(builder: (context) {
return WebViewPage(title: item['title']);
}));路由传参数的话,在返回的 Widget 中写好属性,并在构造函数中指定即可
import 'package:flutter/material.dart';
class WebViewPage extends StatefulWidget {
final String title;
const WebViewPage({super.key, required this.title});
@override
State<StatefulWidget> createState() => _WebViewPageState();
}
class _WebViewPageState extends State<WebViewPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
// 这里可以使用
appBar: AppBar(title: Text(widget.title)),
body: SafeArea(
child: Container(
child: InkWell(
onTap: () {
Navigator.pop(context);
},
child: Container(),
),
),
),
);
}
}
状态管理
StatelessWidget 和 StatefulWidget 状态组件
在 Flutter 中,StatelessWidget 和 StatefulWidget 是用于创建用户界面的两种基本类型的组件(小部件)。它们之间的主要区别在于状态(state)的可变性。
StatelessWidget 无状态组件
不可变:一旦构建完成,其配置和属性就不会改变。
用途:适用于展示静态内容或简单的用户界面元素,例如图标、文本、按钮等,这些内容在应用生命周期中不需要更新。
实现:当想要创建一个不随状态变化而变化的组件时,继承自 StatelessWidget 并实现 build 方法。
class MyStatelessWidget extends StatelessWidget {
final String title;
MyStatelessWidget({required this.title});
@override
Widget build(BuildContext context) {
return Text(title);
}
}StatefulWidget 有状态组件
可变:在其生命周期内可以保持状态,并且这些状态可以随时间发生变化。
用途:适合需要根据用户交互或其他因素动态更新UI的组件。例如,复选框、动画、表单输入等。
实现:
需要两个类:一个是继承自 StatefulWidget 的类,另一个是继承自 State 的类。前者负责创建状态对象,而后者负责维护状态并实现 build 方法。
class MyStatefulWidget extends StatefulWidget {
@override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int counter = 0;
void _incrementCounter() {
setState(() {
counter++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Counter: $counter'),
ElevatedButton(
onPressed: _incrementCounter,
child: Text('Increment'),
),
],
);
}
}
父子组件传值
功能实现
滑动切换 BottomBar
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
late PageController _pageController;
int _currentIndex = 0;
@override
void initState() {
super.initState();
_pageController = PageController(); // 正确初始化
}
@override
void dispose() {
_pageController.dispose(); // 释放资源
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
controller: _pageController,
onPageChanged: (index) {
setState(() {
_currentIndex = index;
});
},
children: const <Widget>[
Center(child: Text('Home Page')),
Center(child: Text('Search Page')),
Center(child: Text('Settings Page')),
],
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
_pageController.animateToPage(
index,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Search',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Settings',
),
],
),
);
}
}瀑布流
此处为语雀内容卡片,点击链接查看:https://www.yuque.com/chen-bo-bo/fe/se39p4druprn7yz4
踩坑日记
iOS 运行失败
出现问题:
参考:https://stackoverflow.com/questions/79118572/xcode-16-and-ios-18-project-not-compiling