目录
本人会针对Flutter从入门到相对熟练【具备开发水平】,在掘金上开辟一个专栏,专门讲述掌握Flutter的基本过程,每周会定期更新1-2篇博客!
本人会不断提供优质的内容博客给大家,内容目前涵盖Objective-C、Swift、Flutter、小程序的开发。欢迎点赞博客及关注本人,共同进步~~~
问题
本系列文章基于最新发布的flutter 版本,开发工具为Android Studio,这是flutter从0到1的「工程目录结构解析」。
搭建好Flutter的开发环境,并且创建项目first_flutter。对于first_flutter项目,项目入口是什么?结构又是什么?我们写的代码放在哪里等问题。带着这些问题,我们一一解决!!!
一 功能结构与作用
1.1 功能结构
查看创建的first_flutter项目,项目结构如下图:
1.2 作用
下面查看结构模块中文件夹内容的作用:
-
dart_tool:dart依赖的第三方库,当第三方库下载下来的时候,会生成dart_tool文件。里面有package_config.json【记录某些文件所在的位置和版本信息相关内容,这个文件夹不需要开发人员动】
-
idea文件:Android Studio是谷歌基于IDEA开发的,作用是对当前项目做一些配置。
-
android文件:Andriod 平台相关代码。
-
iOS文件:iOS平台相关代码【是可以直接运行起来的,包括iOS真机的配置也要用到这个文件】
-
lib文件:我们研发写的代码主要都在这里,功能及UI等。
-
test文件:widget_test.dart-组件的测试方面。
-
gitignore:做一些git的忽略【因为在往git服务器提交东西的时候,有些东西需要忽略,这个文件就有用啦】
-
metadata:对flutter版本做一个记录【更新操作,访问能力等,本文件不能手动更改】
-
packages:相关依赖库地址
-
firstflutter:相关flutter配置
-
pubspec.lock:相关依赖的书写
-
pubspec.yaml:相关依赖库的书写
-
readme:项目描述
二 项目代码
2.1 first_flutter代码
当新建完项目时,查看main.dart文件,会看到比较简洁的代码【去除注释后】,如下
程序的入口是main.dart【此文件不能更改文件名字,Flutter内部已经默认从main.dart加载程序】里面的main函数。
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
2.2 first_flutter运行效果
-
这是一个计数器的案例程序,点击右下角的 + 符号,上面显示的数字会递增;
-
但是我们第一次接触main.dart中的代码,可能会发现很多不认识的代码,不知道这个内容是如何编写出来的;
作为初学者,我的建议是将其中所有的代码全部删除掉,从零去创建里面的代码,这样我们才能对Flutter应用程序的结构非常清晰,下面开始:
三 进阶一
在这以前,如果对Dart还不熟悉,请先阅读上篇文章 Flutter初体验 -- 五分钟搞定Dart知识
做任何的开发,都是从祖传的Hello World
开始,那么现在的需求来了---在界面中心位置,显示一个Hello World 你好
3.1 代码实现
import 'package:flutter/material.dart';
main() {
runApp(Text("Hello World 你好", textDirection: TextDirection.ltr));
}
3.2 界面如下
3.3 代码疑问点
当然,上面的代码已经实现了在界面上显示Hello World 你好
- 但是没有居中,字体也有点小;
- 这些问题,放到后面再来解决,先搞懂目前的几行代码;
上面的代码有一些比较熟悉,有一些并不清楚是什么:
-
比如知道Dart程序的入口都是main函数,而Flutter是Dart编写的,所以入口也是main函数;
-
但是导入的Material是什么呢;
-
在main函数中调用了一个runApp()函数又是什么呢?
3.4 代码分析
3.4.1 runApp和Widget
runApp是Flutter内部提供的一个函数,当启动一个Flutter应用程序时就是从调用这个函数开始的,可以点到runApp的源码,查看到该函数代码如下:
void runApp(Widget app) {
...省略代码
}
该函数让传入一个东西:Widget?
拓展:先说widget的翻译
1.Widget在国内有很多的翻译;
2.做过Android、iOS等开发的人群,喜欢将它翻译成控件;
3.做过Vue、React等开发的人群,喜欢将它翻译成组件;
4.如果我们使用Google,Widget翻译过来应该是小部件;
5.没有说哪种翻译一定是对的,或者一定是错的,但是我个人更倾向于小部件或者组件;
Widget到底什么东西呢?
-
学习Flutter,从一开始就可以有一个基本的认识:Flutter中万物皆Widget(万物皆可盘);
-
在iOS或者Android开发中,界面有很多种类的划分:应用(Application)、视图控制器(View Controller)、活动(Activity)、View(视图)、Button(按钮)等等;
-
但是在Flutter中,这些东西都是不同的Widget而已;
-
也就是整个应用程序中所看到的内容几乎都是Widget,甚至是内边距的设置,我们也需要使用一个叫Padding的Widget来做;
runApp函数让我们传入的就是一个Widget:
-
但是现在没有Widget,怎么办呢?
-
导入Flutter默认已经给我们提供的Material库,来使用其中的很多内置Widget;
3.4.2 Material设计风格【导入库,runApp全局函数在Material库中】
material是什么呢?
-
material是Google公司推行的一套设计风格,或者叫设计语言、设计规范等;
-
里面有非常多的设计规范,比如颜色、文字的排版、响应动画与过度、填充等等;
-
在Flutter中高度集成了Material风格的Widget;
-
在应用中,可以直接使用这些Widget来创建我们的应用(后面会用到很多);
Text小部件分析:【Text()代码分析】
class Text extends StatelessWidget {
const Text(
this.data, {
Key key,
this.style,
this.strutStyle,
this.textAlign,
this.textDirection,
this.locale,
this.softWrap,
this.overflow,
this.textScaleFactor,
this.maxLines,
this.semanticsLabel,
this.textWidthBasis,
});
}
-
可以使用Text小部件来完成文字的显示;
-
发现Text小部件继承自StatelessWidget,StatelessWidget继承自Widget;
-
所以可以将Text小部件传入到runApp函数中
-
属性非常多,但是已经学习了Dart语法,所以会发现只有this.data属性是必须传入的。
StatelessWidget简单介绍:
-
StatelessWidget继承自Widget;
-
后面会更加详细的介绍它的用法;
abstract class StatelessWidget extends Widget { // ...省略代码 }
四 进阶二
4.1 优化一:居中
发现现在的代码并不是想要的最终结果:
-
可能希望文字居中显示,并且可以大一些;
-
居中显示: 需要使用另外一个Widget,Center;
-
文字大一些: 需要给Text文本设置一些样式;
修改代码如下:
-
在Text小部件外层包装了一个Center部件,让Text作为其child;
-
并且,给Text组件设置了一个属性:style,对应的值是TextStyle类型;
代码实现
import 'package:flutter/material.dart';
void main() {
// 第一步: 调用runApp【flutter提供的一个全局函数】,runApp函数是在头文件的库中,所以要导入package:flutter/material.dart
//runApp需要传入Widget app【万物皆Widget】,Widget是抽象类,是不能够实例化的,所以用子类实例化
runApp(
Center(
child: Text(
"Hello World",
textDirection: TextDirection.ltr,
style: TextStyle(
fontSize: 30,
color: Colors.orange,
),
),
)
);
}
界面如下
4.2 优化二:改善界面结构
目前虽然可以显示HelloWorld,但是发现最底部的背景是黑色,并且我们的页面并不够结构化。
正常的App页面应该有一定的结构,比如通常都会有导航栏,会有一些背景颜色等
在开发当中,并不需要从零去搭建这种结构化的界面,可以使用Material库,直接使用其中的一些封装好的组件来完成一些结构的搭建。
代码实现
import 'package:flutter/material.dart';
main() {
// 第一步: 调用runApp【flutter提供的一个全局函数】,runApp函数是在头文件的库中,所以要导入package:flutter/material.dart
//runApp需要传入Widget app【万物皆Widget】,Widget是抽象类,是不能够实例化的,所以用子类实例化
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("第一个flutter项目"),
),
body: Center(
child: Text(
"Hello World 你好",
textDirection: TextDirection.ltr,
style: TextStyle(
fontSize: 30,
color: Colors.orange,
),
),
) ,
)
)
);
}
界面如下
代码剖析
在最外层包裹一个MaterialApp
-
这意味着整个应用都会采用MaterialApp风格的一些东西,方便我们对应用的设计,并且目前使用了其中两个属性;
-
title:这个是定义在Android系统中打开多任务切换窗口时显示的标题;(暂时可以不写)
-
home:是该应用启动时显示的页面,传入了一个Scaffold;
Scaffold是什么呢?
-
翻译过来是脚手架,脚手架的作用就是搭建页面的基本结构;
-
所以给MaterialApp的home属性传入了一个Scaffold对象,作为启动显示的Widget; Scaffold也有一些属性,比如appBar和body;
-
appBar是用于设计导航栏的,传入了一个title属性;
-
body是页面的内容部分,传入了之前已经创建好的Center中包裹的一个Text的Widget;
4.3 优化三:界面承载更多元素
我们可以让界面中存在更多的元素:
代码实现
import 'package:flutter/material.dart';
main() {
// 第一步: 调用runApp【flutter提供的一个全局函数】,runApp函数是在头文件的库中,所以要导入package:flutter/material.dart
//runApp需要传入Widget app【万物皆Widget】,Widget是抽象类,是不能够实例化的,所以用子类实例化
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("第一个flutter项目"),
),
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Checkbox(
value: true,
onChanged: (value) => print("Hello World")),
Text(
"同意协议",
textDirection: TextDirection.ltr,
style: TextStyle(fontSize: 20),
)
],
),
),
),
)
);
}
界面如下
看到上面截图是不是右上角都有一个Debug标识,通过在MaterialApp里面加入一行代码:
debugShowCheckedModeBanner: false,
看到上面的代码逻辑,是不是觉得在一个方法里面做了很多的事情,而且代码看起来很难维护和封装,下面我们对代码进行重构!!!
五 项目重构
5.1 创建自己的widget
很多学习Flutter的人,都会被Flutter的嵌套劝退,当代码嵌套过多时,结构很容易看不清晰。
Flutter整个开发过程中就是形成一个Widget树,所以形成嵌套是很正常的。
但是,开发一个这么简单的程序就出现如此多的嵌套,如果应用程序更复杂呢?
-
可以对我们的代码进行封装,将它们封装到自己的Widget中,创建自己的Widget; 如何创建自己的Widget呢?
-
在Flutter开发中,可以继承自StatelessWidget或者StatefulWidget来创建自己的Widget类;
-
StatelessWidget: 没有状态改变的Widget,通常这种Widget仅仅是做一些展示工作而已; StatefulWidget: 需要保存状态,并且可能出现状态改变的Widget;
在上面的案例中对代码的重构,使用StatelessWidget即可,所以接下来学习一下如果利用StatelessWidget来对我们的代码进行重构;
StatefulWidget放到后面的一个案例中来学习;
5.2 StatelessWidget
StatelessWidget通常是一些没有状态(State,也可以理解成data)需要维护的Widget:
来看一下创建一个StatelessWidget的格式:
-
让自己创建的Widget继承自StatelessWidget;
-
StatelessWidget包含一个必须重写的方法:build方法;
build方法什么情况下被执行呢?
-
当我们的StatelessWidget第一次被插入到Widget树中时(也就是第一次被创建时);
-
当我们的父Widget(parent widget)发生改变时,子Widget会被重新构建;
-
如果我们的Widget依赖InheritedWidget的一些数据,InheritedWidget数据发生改变时;
5.3 重构案例代码
就可以通过StatelessWidget来对我们的代码进行重构了
因为我们的整个代码都是一些数据展示,没有数据的改变,使用StatelessWidget即可;
另外,为了体现更好的封装性,我对代码进行了两层的拆分,让代码结构看起来更加清晰;(具体的拆分方式,在后面的案例中不断的体现出来,目前先拆分两层)
import 'package:flutter/material.dart';
//使用箭头函数,当只有一个表达体时
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: Text("第一个Flutter项目"),
),
body: HomeContent(),
),
);
}
}
class HomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Checkbox(
value: true,
onChanged: (value) => print("Hello World")),
Text(
"同意协议",
textDirection: TextDirection.ltr,
style: TextStyle(fontSize: 20),
)
],
),
);
}
}
通过以上上面的学习和了解,相信再次看first_flutter项目的代码会有比较清晰的认识【起码不会摸不到头绪吧】
六 Go Back
下面是first_flutter项目代码的部分注解【再次 回看代码,会有比较清晰的理解】
import 'package:flutter/material.dart';
// 等价于main() => runApp(MyApp()); 箭头函数的使用
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
//对于主题颜色设置
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
/**
* StatefulWidget: 分为两个类
* 1.集成自StatefulWidget的类【作用:1. 有一个必须实现的方法,如下:createState() 2. 可以接收父widget传过来的数据】
* 2.对应着有state这个类【作用是保存状态】
*/
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
//下一篇博客会着重讲述StatefulWidget和StatelessWidget以及state
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
//实现++功能
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
//点击右下角的按钮,实现自加
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
机会❤️❤️❤️🌹🌹🌹
如果想和我一起共建抖音,成为一名bytedancer,Come on。期待你的加入!!!
总结
今天这篇文章,详细介绍了Flutter项目的整体框架和结构以及代码讲述,也讲述了如何从0-1开始书写Flutter项目,以及项目中运用的知识点和技能点。大家可以手动的编写上面的Demo例子,相信通过上面的例子以及进阶,可以加深大家对Flutter项目的认知和感触!!!
下一篇我们将讲述Flutter实现列表的滚动和展示【也会着重讲解StatelessWidget和StatefulWidget】
希望对大家有所帮助,也感谢大家的点赞作品及关注本人,共同进步,共勉!!!