Flutter 第一个程序Hello World!

2022-09-09 09:38:35 浏览数 (1)

Flutter 第一个程序Hello World!

  • 前言
  • 正文
    • 一、Flutter SDK下载
    • 二、环境变量配置
    • 三、Android Studio 开发环境
    • 四、运行hello_world项目
    • 五、创建Flutter项目
    • 六、Flutter工程结构
    • 七、Flutter开发核心思想
  • 尾声

前言

  Flutter实际上在我学习Android之前就已经听说过了,不过那时候的Flutter还是初始版本,并不如原生,虽说有跨平台的优势,但也只是了解而已,没有去正式使用,那么为什么又要学习了呢?

正文

  因为时代又进了一步,现在流行内卷,你不卷别人,别人就要卷你。没办法!以往学习Flutter的大多数是前端转的,而现在大部分都是熟悉Android或者IOS的开发工程师在公司预算不足以招满两个端的前景下,去学习Flutter,怎么说呢?行走江湖,技多不压身,你可以不用精通,但你得会,不说别的,起码能让你在简历的技能栏上多写上一行,何乐而不为呢?

  下面我们开始接触Flutter,首先你需要知道Flutter的官网地址:Flutter官网,Flutter中文官网,在学习过程中很多资料你都可以通过官网去查询,你所遇到的任何问题都能解决,只不过刚开始对你来说你需要走很多的弯路。

  Flutter开发使用的是Dart语言,不用害怕,实际上并不难。目前最新的Flutter版本是3.x.x,在我刚听说Flutter的时候还是1.0版本,那个时候使用起来其实效果不是很好,缺少很多的依赖支持库,而且和原生的差距比较大,而在2.0的时候Flutter就已经有很大的优化了,这时候开发者和开源项目如雨后春笋一般猛增,而现在已经到了3.0,很多公司会直接在招人要求上写Flutter技能,为什么想必就不用我多说了。

一、Flutter SDK下载

  Flutter作为跨平台的技术,可以在Windows、macOS、Linux、Chrome OS上安装,我这里使用的是Windows11,等我有钱了,我高低弄一个macOS,到时候Android Studio XCode都能配置起来。 下面先下载Flutter SD,地址:Flutter SDK,在里面可以看到最新的SDK版本。

  我这里看到最新的是3.0.5,这个版本更新的还是很频繁的,点击这个3.0.5就会弹窗下载。

  还挺大的,下载好之后,解压到指定的路径下,最好不要放在C盘,即使放在C盘也不要放在高级权限的路径中,比如User下。而我就直接解压在D盘中,如下图所示:

默认的文件夹是flutter,我这里改成了Flutter,看个人习惯。

二、环境变量配置

  右键点击计算机图标,依次选择属性 → 高级系统设置 → 高级 → 环境变量。

  环境变量有两种,一种是用户变量,一种是系统变量,我们只需要配置用户变量。点击新建按钮,输入变量名和变量值。

代码语言:javascript复制
变量名:PUB_HOSTED_URL
变量值:https://pub.flutter-io.cn

输入完成之后点击确定,这个变量就保存了。

还有一个变量,继续点击新建。

代码语言:javascript复制
变量名:FLUTTER_STORAGE_BASE_URL
变量值:https://storage.flutter-io.cn

如下图所示操作。

  上面这里的用户环境变量配置是镜像配置,Flutter 源站在国内可能不太稳定,因此谷歌中国开发者社区(GDG)专门搭建了临时镜像,使得我们的 Flutter 命令行工具可以到该镜像站点下载所需资源。

  最后我们配置SDK的路径

  选中用户变量的Path,点击编辑,会弹出一个窗口,先不管它。我们进入到刚才的Flutter目录下的bin文件中。

  需要将这个文件夹路径复制一下:也就是 D:Flutterbin,回到之前的那个窗口。

  点击新建,粘贴刚才SDK路径,点击确定。然后关闭掉当前的所有窗口,重启你的电脑,记得加一个收藏,这样你重启电脑之后还能找到这篇文章,重启电脑后进入第三步。

三、Android Studio 开发环境

  作为Android开发人员,你首先要确保你的Android Studio没有问题,然后才是在Android Studio上配置Flutter的开发环境,下面我们先通过命令行检测一下。Win R回车,输入cmd,回车,输入flutter doctor,回车。

然后再往下滑动看看。

  这条指令会检查电脑上的环境,Android Studio是没有问题的,连接设备也没有问题,网络也没有问题,现在我们的Android Studio还不支持Flutter的,因此我们需要支持它,打开Android Studio。File → Settings → Plugins ,输入Flutter。

  可以看到Flutter实际上作为插件进行安装,我们点击Install进行安装,会弹出一个弹窗。

  提示你安装Flutter之前需要安装Dart插件,因为Flutter使用的是Dart语言,因此点击Install让他去安装。

  安装好之后点击Restart IDE重新启动Android Studio,让我们刚才安装的插件生效。

四、运行hello_world项目

  我们之前下载的Flutter SDK里面有一个examples文件夹,里面是一些flutter项目,这些项目有什么作用呢?首先是让你运行来检测本地的Flutter环境配置,然后就是可以让你快速的了解Flutter。

我们通过Android Studio打开hello_world。点击Flie → Open。

点击OK。

点击Trust Project。

  这就是我们的Flutter项目了,目前正在下载配置的内容,请稍等。发现项目有错误,我们打开lib下的main.dart

  这里提示你Dart SDK 没有配置,而其实我们下载Flutter SDK里面就带了Dart的SDK,因此我们先配置Flutter SDK,在Android Studio中配置Flutter的SDK,如图所示。

  配置好之后点击Apply按钮,再点击OK关闭这个窗口,你会看到当前的hello_world项目会再编译一次,我们再看main.dart。

  点击Upgrade dependencies,更新依赖,更新完成之后,当前的main.dart中的内容就不会报错了。

  然后要运行起来就需要连接真机或者启动虚拟机了。启动模拟器之后我们发现了一个问题。

  项目中似乎没有识别到这个模拟设备,这个时候要看是不是模拟器有问题,于是我打开Andoid项目,发现模拟机是可以识别到,那么问题就出来Flutter上,所以我们要为Flutter配置Android 的Sdk路径,关闭Android Studio,找到Android Sdk路径,我的Android Sdk所在路径是:D:AndroidSdk,然后我们Win R 弹窗,输入cmd,然后进入命令窗口,输入如下指令:

代码语言:javascript复制
flutter config --android-sdk D:AndroidSdk

回车

  配置好之后,这里会提示你重启编辑器,也就是Android Studio,我们重启Android Studio。再启动模拟器。

找到了设备。

好了,下面运行一下。

  看日志好像和这个FlutterActivity有关,我们可以在AndroidManifest.xml中看到有注册这个FlutterActivity

  Targeting S (version 31 and above) requires that an explicit value for android:exported be defined when intent filters are present

  这里看这句话,定位 S (版本 31 及更高版本)要求在存在意图过滤器时定义 android:exported 的显式值,因为我们的虚拟机是Android 12,而在Android12中,注册Activity时要加上android:exported属性,一般启动的设置为ture,其他设置为false,那么我们设置一下看看。

再运行一下

  运行起来了,下面我们再用真机运行试试看,通过USB数据线连接真机。

运行:

也可以运行成功。

  通过运行sdk中自带的项目我们解决了一些问题,同时还发现这个项目比较老旧了,没有做更新,它里面还是基于Android 10去写的,Android11上运行应该没有问题,而到了Android12上就不行了,好了,不研究它的demo了,下面我们要自己创建一个Flutter项目。

五、创建Flutter项目

点击File → New → New Flutter Project。

选择Flutter,点击Next。

创建一个HelloWrold项目。

  这里的项目名称必须以小写,下划线格式进行命名,让我觉得有一些不舒服,这里我修改了项目的存放路径,然后默认选择Android和iOS平台,语言使用Kotlin 和Swift,点击Finish。

项目创建完成,如下图所示:

创建完成之后我们直接运行这个项目在模拟器或者真机上。

这是一个计数器,点击右下角的浮动按钮,屏幕中间的数字会加1。

六、Flutter工程结构

  现在工程已经运行起来了,对于一个新的项目工程,我们需要大概的之后它的结构内容,各个目录代表什么意思。

首先我们来看一下重点内容项目的目录。

.dart_tool

  这是一个dart工具文件夹,里面包含了flutter工程的构建信息,里面还有一个version文件,说明当前使用的flutter的版本,无需什么改动,了解就好。

.idea

  因为Android Studio 是由IDEA编辑器改过来的,因此会在创建项目时生成一个.idea文件夹,根据创建项目类型不同,它里面的内容就会不同,了解就好。

android

  Android的项目文件,作为Android开发者,想必你肯定知道这个android文件夹中的各个文件代表什么意思。

ios

  ios的项目文件,作为Android开发者,我不知道里面怎么操作的也很正常,嗯,暂时我们不考虑ios的问题。

lib

  这是Flutter应用源文件,里面有一个main.dart是程序入口文件,我们运行看到的第一个页面就在这里面,稍后会详细讲述这个main.dart文件。

test

  测试文件

.gitignore

  git忽略文件,就是这里面的文件在提交git时会忽略掉,一般来说就是一些编译时文件,例如build之类的。

.metadata

  用来记录Flutter 项目属性的的隐藏文件。

.packages

用来记录Flutter项目的包信息。

analysis_options.yaml

  静态分析文件。

hello_world.iml

  工程配置文件。

pubspec.lock

  记录当前项目实际依赖信息的文件。

pubspec.yaml

  管理第三方库及资源的配置文件。

README.md

  项目描述文件。

  基本的内容就说完了,这样看起来实际上Flutter工程就是一个同时内嵌了 Android 和 iOS 原生子工程的父工程,我们在 lib 目录下进行 Flutter 代码的开发,而某些特殊场景下的原生功能,则在对应的 Android 和 iOS 工程中提供相应的代码实现,供对应的 Flutter 代码引用。

  Flutter 会将相关的依赖和构建产物注入这两个子工程,最终集成到各自的项目中。而我们开发的 Flutter 代码,最终则会以原生工程的形式运行。

七、Flutter开发核心思想

  我们运行程序之后发现是一个计数器Demo,在这个简单示例中,从基础的组件、布局到手势的监听,再到状态的改变,Flutter 最核心的思想在这 60 余行代码中展现得可谓淋漓尽致,也就是这个lib下的main.dart文件。

  我们来了解一下它在这里面做了什么?首先我们看一下main.dart中的代码:

代码语言:javascript复制
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  
  final String title;

  @override
  State<MyHomePage> 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>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

  这个main.dart里面有很多的注释,我这里是把注释去掉了,你可以自己去了解它们的意思,从而了解这些代码是干什么的。

我们从上往下来看

代码语言:javascript复制
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

  这里首先我们知道导了一个包,material是一个材料设计库,作为Android开发者你不会陌生,这说明这个页面是按照material风格设置的,然后是一个main()函数,里面通过runApp()函数执行一个app部件,也就是Widget,这里的部件就是MyApp()函数,然后我们再看MyApp()做了什么?

代码语言:javascript复制
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

  这里我们的MyApp是一个类,继承了StatelessWidget,这是一个无状态部件,然后实现构造方法,构造方法里面通过MaterialApp()函数定义风格,然后是标题、主题和主页面信息,这里主页面home中调用MyHomePage()函数,也就是我们当前页面所显示的内容。这里有一个Colors.blue,你试一下改成red,或者green。如果你这时候项目是运行在模拟器上或者真机上的话,你可以修改后Ctrl S 进行保存。

  然后就会直接将你刚才改动的渲染到设备上,这叫热重载,这是Flutter中很方便的一个功能,还有一点就是,你注意到模拟器上方这个黄色的闪电图标没有。

  你改动后,点击它和Ctrl S效果是一样的。比如我们再改成green,然后点击这个闪电图标。

然后我们继续往下看。

代码语言:javascript复制
class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

  这里MyHomePage继承StatefulWidget,这是一个有状态的部件,这里就需要一个状态了,通过createState()得到一个_MyHomePageState,这个_MyHomePageState()就是这个页面的主要内容了,它里面是

代码语言:javascript复制
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>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

  这里_MyHomePageState 继承了State< MyHomePage >,创建一个_counter 变量,默认是0,我们运行时看到的就是0,通过_incrementCounter()函数调用setState(),再这里面进行_counter自增,再往下看就是build()构造函数,在 build 方法中,我们通常通过对基础 Widget 进行相应的 UI 配置,或是组合各类基础 Widget 的方式进行 UI 的定制化。

  这里返回一个Scaffold,这是一个脚手架,用来构建页面,如果你有过Compose的开发经验,那么你会觉得熟悉,当然了Compose其实就是向Flutter看齐,只不过Compose是在Android原生中进行声明式UI设计,简化页面,大体思路和Flutter差不多。

  然后我们看Scaffold中的内容,AppBar 是页面的导航栏,我们直接将 MyHomePage 中的 title 属性作为标题使用。body 则是一个 Text 组件,显示了一个根据 _counter 属性可变的文本:‘You have pushed the button this many times:$_counter’。floatingActionButton,则是页面右下角的带“ ”的悬浮按钮。我们将 _incrementCounter 作为其点击处理函数。

  这里主要的内容是值的变化和浮动按钮的点击,也就是$_counter进行赋值,onPressed表示浮动按钮按下,按下后会执行_incrementCounter,然后调用setState函数,setState 函数是 Flutter 以数据驱动视图更新的关键函数,它会通知 Flutter 框架,因为它里面_counter ,所以数据发生变化,通过刷新界面。而 Flutter 框架收到通知后,会执行 Widget 的 build 方法,根据新的状态重新构建界面。

  这个思路其实你可以类比DataBinding,页面和数据相关,但是它是单向的,通过状态来控制页面UI改变,这就是显示比较新的一个框架了,MVI框架。

  如果你深刻的理解了这个实例,那么你就知道了Flutter的核心思想,不要去计较某一个API的使用方式,陷在里面就得不偿失了,从全局出发。

尾声

  开始Flutter的学习之旅,你是否感觉到兴奋呢?

0 人点赞