Flutter 初学Flutter页面开发
一. 创建Flutter项目
1.1. 创建
命令行方式
flutter create learn_flutter
注意:
- Flutter的名称不要包含特殊的字符,另外 <font color=red>不可以使用驼峰,不支持大写,尽量不要使用一些符号</font> 标识
- 创建完之后使用自己喜欢的开发工具打开即可
- xxx_xxx 下划线格式较多一些
工具创建
如:android studio - Start a new Flutter project
1.2. 运行
运行
-
命令行方式,进入flutter项目目录:flutter run即可
-
android studio 打开项目,可以使用ide提供的快捷工具直接运行,as也是可以运行ios模拟器的
-
也可以通过as或xcode分别打开flutter项目中的android或ios代码,直接运行即可。
-
as也可以直接打开整个flutter项目代码
断点
vscode dart代码支持断点调试的,也是代码面板左边点击某行代码打上断点,然后run - start debugging
二. 目录结构
文件或目录 | 说明 |
---|---|
.dart_tool | 记录了一些dart工具库所在的位置和信息 |
.idea | android studio 是基于idea开发的,.idea 记录了项目的一些文件的变更记录 |
android | Android项目文件夹 |
ios | iOS项目文件夹 |
lib | lib文件夹内存放我们的dart语音代码 |
test | 用于存放我们的测试代码 |
.gitignore | git忽略配置文件 |
.metadata | IDE 用来记录某个 Flutter 项目属性的的隐藏文件 |
.packages | pub 工具需要使用的,包含 package 依赖的 yaml 格式的文件 |
flutter_app.iml | 工程文件的本地路径配置 |
pubspec.lock | 当前项目依赖所生成的文件 |
pubspec.yaml | 当前项目的一些配置文件,包括依赖的第三方库、图片资源文件等 |
README.md | READEME文件 |
重要的文件就以下几个:
- lib
我们日常开发的dart语言代码都放在这里,可以说是我们的“核心工作文件夹”
- iOS
这里面包含了iOS项目相关的配置和文件,当我们的项目需要打包上线的时候,需要打开该文件内的Runner.xcworkspace文件进行编译和打包工作。
- android
与ios文件夹一样,在android项目需要打包上架的时候,也需要使用此文件夹里面的文件。同样的如果我们需要原生代码的支持,原生代码也是放在这里。
- test
这里存放了我们在项目开发过程中的测试代码,良好的测试习惯是保证代码质量的必要手段,希望大家在test文件里写更多的代码!
- pubspec.yaml
就好比开发原生,你总要知道工程如何配置,依赖三方,资源文件等
2.1. main.dart
flutter 默认入口文件,内部main函数是flutter解析的入口
关于默认的main.dart文件:
1、程序默认入口文件,这个是可配置的,如as开发环境:可以通过Edti Configuratios来修改入口文件。。。也可以通过命令行:flutter run -t lib/entry.dart
2、虽然入口文件能修改,但是一般不用,并且注意,入口文件内必须有main函数主入口的,这个跟android,ios之类的一样
三. hot reload & hot restart
冷启动
从零编译运行,耗时
hot reload
主要执行build:
@override
Widget build(BuildContext context) {
意思就是如果修改的是build方法体内的内容,只需要hot reload即可
hot restart
重新运行整个app,如果修改的是非build内容,则需要hot restart
四. flutter
4.1. 基本页面
firstapp_helloworld.pngvoid main() {
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('title'),
),
body: Center(
child: Text(
'hello world',
style: TextStyle(
color: Colors.amber,
fontSize: 50
),
strutStyle: StrutStyle(
fontSize: 10,
fontStyle: FontStyle.italic,
),
textDirection: TextDirection.ltr,
// textScaleFactor: 2,
)
),
bottomNavigationBar: BottomAppBar(
color: Colors.red,
child: Text(
'bottom'
),
),
)
)
);
}
4.2. 代码分析
runApp
runApp是Flutter内部提供的一个函数,当我们启动一个Flutter应用程序时就是从调用这个函数开始的
- 我们可以点到runApp的源码,查看到该函数
该函数让我们传入一个东西:Widget?
widget
- 我们学习Flutter,从一开始就可以有一个基本的认识:Flutter中万物皆Widget(万物皆可盘);
- 在我们iOS或者Android开发中,我们的界面有很多种类的划分:应用(Application)、视图控制器(View Controller)、活动(Activity)、View(视图)、Button(按钮)等等;
- 但是在Flutter中,这些东西都是不同的Widget而已;
- 也就是我们整个应用程序中所看到的内容几乎都是Widget,甚至是内边距的设置,我们也需要使用一个叫Padding的Widget来做;
material
- material是Google公司推行的一套设计风格,或者叫设计语言、设计规范等;
- 里面有非常多的设计规范,比如颜色、文字的排版、响应动画与过度、填充等等;
- 在Flutter中高度集成了Material风格的Widget;
- 在我们的应用中,我们可以直接使用这些Widget来创建我们的应用(后面会用到很多);
cupertino.dart 风格
前端开发,ui是主要部分,上面的Material提供了丰富的ui组件,cupertino风格是苹果风格的,但不是很丰富,可以尝试用
Scaffold 脚手架
- 翻译过来是脚手架,脚手架的作用就是搭建页面的基本结构;
- 所以我们给MaterialApp的home属性传入了一个Scaffold对象,作为- 启动显示的Widget;
- Scaffold也有一些属性,比如appBar和body;
- appBar是用于设计导航栏的,我们传入了一个title属性;
- body是页面的内容部分,我们传入了之前已经创建好的Center中包裹的一个Text的Widget;
4.3. 代码重构
一个简单的页面,嵌套写了那么多层的代码,很不友好,所以,正常的代码怎么做的?
flutter中万物皆widget,也即分析页面后,可以将页面元素拆分,比如:导航条,body体,底部视图等。然后各个部分抽离出来封装为新的组件
4.3.1. 封装widget
首先,新入门语言,都会觉得有些不是因,要做的是需要熟悉写法,学会该语言的代码结构,然后熟练就好了。
关于flutter的widget,先说明一下:
- Flutter整个开发过程中就是形成一个Widget树,所以形成嵌套是很正常的。
- 封装widget有两种类型,可以继承自StatelessWidget或者StatefulWidget
这里先不深入如何完全自定义widget,先学会小视图组合方式的封装,把代码重构清晰
4.3.1.1. StatelessWidget
StatelessWidget是没有状态改变的Widget,通常这种Widget仅仅是做一些展示工作而已;
- 它们的数据通常是直接写死(放在Widget中的数据,必须被定义为final)
- StatelessWidget所继承的Widget被@immutable标记,表示不可变,不可变是指该widget内所有的变量是不可变的,所以,你的widget内声明状态是不合理的。所以印证了上面的话:StatelessWidget类型的widget只做一些纯展示
- 很重要的一点是:自定义widget内没有setState方法,所以,没法进行widget状态管理,触发重新渲染更新
- 从parent widget中传入的而且一旦传入就不可以修改;
- 从InheritedWidget获取来使用的数据;
//1、继承自StatelessWidget
//2、重写的方法:build方法
class MyStatelessWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return <返回我们的Widget要渲染的Widget,比如一个Text Widget>;
}
}
build
- Flutter在拿到我们自己创建的StatelessWidget时,就会执行它的build方法;
- 我们需要在build方法中告诉Flutter,我们的Widget希望渲染什么元素,比如一个Text Widget;
- StatelessWidget没办法主动去执行build方法,当我们使用的数据发生改变时,build方法会被重新执行;
build 什么情况会执行:
- 1、当我们的StatelessWidget第一次被插入到Widget树中时(也就是第一次被创建时);
- 2、当我们的父Widget(parent widget)发生改变时,子Widget会被重新构建;
- 3、如果我们的Widget依赖InheritedWidget的一些数据,InheritedWidget数据发生改变时;
重构结果
篇幅原因,部分代码就不展示了
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage()
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('title'),
),
body: HomeBody(),
bottomNavigationBar: HomeBottomBar(),
);
}
}
class HomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Text(
'hello world',
...
)
);
}
}
class HomeBottomBar extends StatelessWidget {...}
4.3.1.2. StatefulWidget
StatefulWidget是需要保存状态,并且可能出现状态改变的Widget;前面说了,StatelessWidget是纯展示的一个widget,也即其是没办法做状态管理的,所以,其上内容是没法更新的。就做一些静态展示。。与其相对的额StatefulWidget就包含了状态管理,做数据展示和数据操作