• Flutter 第一个程序Hello World!


    前言

      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,看个人习惯。

    二、环境变量配置

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

    在这里插入图片描述

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

    变量名:PUB_HOSTED_URL
    变量值:https://pub.flutter-io.cn
    
    • 1
    • 2

    在这里插入图片描述

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

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

    变量名:FLUTTER_STORAGE_BASE_URL
    变量值:https://storage.flutter-io.cn
    
    • 1
    • 2

    如下图所示操作。

    在这里插入图片描述

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

      最后我们配置SDK的路径

    在这里插入图片描述

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

    在这里插入图片描述

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

    在这里插入图片描述

      点击新建,粘贴刚才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:\Android\Sdk,然后我们Win + R 弹窗,输入cmd,然后进入命令窗口,输入如下指令:

    flutter config --android-sdk D:\Android\Sdk
    
    • 1

    回车

    在这里插入图片描述

      配置好之后,这里会提示你重启编辑器,也就是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中的代码:

    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),
          ),
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

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

    我们从上往下来看

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

      这里首先我们知道导了一个包,material是一个材料设计库,作为Android开发者你不会陌生,这说明这个页面是按照material风格设置的,然后是一个main()函数,里面通过runApp()函数执行一个app部件,也就是Widget,这里的部件就是MyApp()函数,然后我们再看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'),
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

    在这里插入图片描述

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

    在这里插入图片描述

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

    在这里插入图片描述

    然后我们继续往下看。

    class MyHomePage extends StatefulWidget {
      const MyHomePage({Key? key, required this.title}) : super(key: key);
    
      final String title;
    
      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

      这里MyHomePage继承StatefulWidget,这是一个有状态的部件,这里就需要一个状态了,通过createState()得到一个_MyHomePageState,这个_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),
          ),
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

      这里_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的学习之旅,你是否感觉到兴奋呢?

  • 相关阅读:
    LeetCode第14题:最长公共前缀
    C语言典型例题28
    中国业务型CDP白皮书 | 爱分析报告
    SELinux零知识学习二十、SELinux策略语言之类型强制(5)
    strongswan开源客户端项目排错运行
    SQLITE3【1】-SQLite向arm-imx6ull板子的移植
    字典树——最大异或对(模板)
    JavaSE期末经典项目—>>图书管理系统
    FEDformer 代码分析(1)
    bazel远程缓存(Remote Cache)
  • 原文地址:https://blog.csdn.net/qq_38436214/article/details/125899328