稀土掘金 稀土掘金

flutter快速上手

本文旨在介绍flutter项目的大致流程,与基础语法。

一、安装配置

官方文档: 安装和环境配置

扩展插件:

  • Dart
  • Flutter
  • Flutter Widget Snippets 快捷操作
  • Flutter Intl 语言国际化

遇到的问题:

  • SDK无法下载,项目运行卡住,下载依赖卡住,timeout等
  • 以上80%都是网络导致的

解决方案:

方式一:终端启用代理

  • 科学上网

mac设置代理

export https_proxy=http://127.0.0.1:7890 http_proxy=http://127.0.0.1:7890 all_proxy=socks5://127.0.0.1:7890

方式二:抓包或查看日志文件,配host

  1. 查看日志(这里使用任意抓包工具都可)

  1. 查询ip,配置host

IP查询: IP查询

方式三:配置镜像

  • 具体方法查询各类搜索引擎

二、Dart语言基础

推荐一个在线运行dart的平台: DartPad

  • main函数是项目入口函数,不可或缺。(有点类似java的)
void main() async {
//   // 声明变量
//   // 通用var
//   var v1 = 12; // 此时v1变量固定为int类型
//   v1 = "str"; // 报错
//   // 定义整型
//   int? i1 = 23; // ? 可空
//   // 定义double
//   double d1 = 3.14;
//   // 定义字符串
//   String s1 = "str22";
//   // 定义数组
//   List<int> iList = [2,3,1,2];
//   // 定义map
//   Map<String,int> sMap = {"s1":23,"s2":34};
//   print(sMap["s1"]); // 不能使用sMap.s1调用
  
  
//   // dynamic与object 声明变量
//   // dynamic相当于不声明变量的类型,后期可以重新赋值为不同的类型
//   dynamic d1 = "str";
//   d1 = 12;
//   // 所有类型都是Object的子类,因此可以重新赋值为不同的类型
//   Object o1 ={"xx":343};
//   o1 = 12;
//   // 两者的区别
//   print(d1["xx"]); // 编译时不会报错
//   print(o1["xx"]); // 编译时会报错,只能使用 Object 的属性与方法,
//   print(o1.toString()); // 只能使用 Object 的属性与方法,
  
//   // final和const
//   // final声明的变量只能赋值一次,首次声明可以赋值也可以不赋值
//   late final f1 ;  // 第一次不赋值,最好在前面加上late关键字
//   f1 = "str1";  
//   f1 = "str2"; // 报错,不能第二次赋值
//   // const 声明时就必须赋值
//   const c1 = "ccc";  // 报错,必须赋值
//   const c2;  // 报错,必须赋值
  
//   // 函数声明
//   bool isExist(String arg1,{required double arg2,int? arg3, bool arg4 = false}){
//     return false;
//   }
//   // 调用
//   isExist("arg1",arg2:2.2,arg3:12);
  
//   // class 类
//   // Person1
//   Person1("name",age:13);  //abstract抽象类 不能直接实例化
//   Student s2 = Student('sss',28);
//   s2.walk(); 
  
//   // 异步,Future类似js中的promise
//   Future.delayed(const Duration(seconds: 2),(){
//      return "hi word!";
//   }).then((data){
//      print(data);
//   }).catchError((err){
//     print(err);
//   }).whenComplete((){});
  
//   String data = await Future.delayed(const Duration(seconds: 2),(){
//      return "hi world!";
//   });
//   print(data);
  
//   // Future.wait
//   var start = DateTime.now().millisecondsSinceEpoch;
//   await Future.wait([
//     Future.delayed(const Duration(seconds: 3)),
//     Future.delayed(const Duration(seconds: 3)),
//     Future.delayed(const Duration(seconds: 3)),
//   ]);
//   var end = DateTime.now().millisecondsSinceEpoch;
//   print('执行时间:${end - start}');
  
}

// // abstract抽象类,只能被继承,不能直接实例化
// abstract class Person1 {
//   late final name;
//   late final age;
//   // 构造方法
//   Person1(this.name,{required this.age});
// }

// mixin Eat {
//   eat() {
//     print('eat');
//   }
// }

// mixin Walk {
//   walk() {
//     print('walk');
//   }
// }

// // 继承,mixin类似多继承
// class Student extends Person1 with Eat, Walk{
//   late final name;
//   late final age;
//   // 构造方法
//   Student(this.name,this.age):super(name,age:age);
// }

三、项目搭建

创建项目

方式一:命令行创建

flutter create 项目名字

方式二:android studio创建

项目结构

运行与调试

选择设备

  • 模拟器设备会自动识别,(安卓可通过android studio创建模拟器,ios需要使用xcode创建)
  • 真机设备需要使用开发者模式

运行

# 运行,默认是debug模式
flutter run 
# release运行
flutter run --release
# profile模式运行(只能在真机运行)
flutter run --profile
  • 在终端,使用r热重载,相当于重新执行builder,不会执行maininitStateinitState是有状态组件的某个生命周期)
  • 在终端,使用R热重启,重新运行项目,但不会下载新引入的依赖,若新增依赖,建议关闭运行后重启
  • DEBUG 模式下才能执行热重载或者热重启
  • iOS真机运行,建议使用flutter run --releasedebug模式运行时,退出app重新进入app会崩溃

调试

调试运行后:

  1. 在vscode中,点击vscode左侧的运行与调试,点击create a launch.json,选择dart&flutter
  2. 使用web调试页面:完成上一步后,打开: http://localhost:9100/,输入控制台打印的地址,即可调试

image.png

使用此方式进行调试,每次点击保存都会热重载,不需要输入r。个人比较推荐

四、Flutter项目是如何运行

运行默认的计数器项目:

  • 每次点击+,屏幕上的数字就会加1

lib/main源码:

  • Flutter中几乎所有的元素都是一个widgetwidget可以理解为组件,控件。包括:UI组件,手势监听控件,主题组件等
  • flutter通过widget来构建UI和事件处理,widget中还可以嵌套widget
// 导入了 Material UI 组件库,这是官方写好的库,里面包含了很多实用的widget
import 'package:flutter/material.dart';
// 程序入口
void main() {
  // 运行app,runApp接收一个widge参数
  runApp(const MyApp());
}
// StatelessWidget是无状态组件,不管理状态
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // 通过build方法构建 UI 界面
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo', // app标题
      theme: ThemeData(  // app主题
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        // 使用Material3的样式主题等
        useMaterial3: true,  
      ),
      // 应用首页,MyHomePage
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

// StatefulWidget有状态组件,需要状态管理就需要这个组件,比如这里的_counter就是我们需要管理的状态
class MyHomePage extends StatefulWidget {
  // 构造函数接收一个title参数
  const MyHomePage({super.key, required this.title});
  final String title;

  // 有状态组件需要创建一个State类
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    // setState更改状态,运行setState会重新执行build方法
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        // 使用StatefulWidget中的数据,使用widget获取
        title: Text(widget.title),
      ),
      body: Center( // Center让它的子元素居中
        child: Column(// Column组件让子元素垂直布局,类似css中的弹性布局,垂直方向。如果需要水平布局可以使用Row
          mainAxisAlignment: MainAxisAlignment.center, // 主轴方向居中
          children: <Widget>[
            // const表示该组件是不变的,重新build时不会重新build该组件
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter', // 引用状态
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      // floatingActionButton是浮动按钮
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter, // 点击时执行_incrementCounter方法
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), 
    );
  }
}

分析运行流程:

我们找到浮动按钮的代码:

  • 点击浮动按钮,执行_incrementCounter方法
// floatingActionButton是浮动按钮
floatingActionButton: FloatingActionButton(
  onPressed: _incrementCounter, // 点击时执行_incrementCounter方法
  tooltip: 'Increment',
  child: const Icon(Icons.add),
), 

_incrementCounter方法的代码:

  • setState更改状态,运行setState会重新执行build方法,重绘页面(类似react的setState)
void _incrementCounter() {
  setState(() {
    _counter++;
  });
}

_counter发生改变重新运行build方法,页面发生变化

Text(
  '$_counter', // 此时的_counter就是最新的_counter
  style: Theme.of(context).textTheme.headlineMedium,
),

五、打包上线

ios与android打包都需要配置相关的证书,这里不做细讲,主要分享相关命令与shell脚本

iOS打包:

主要介绍两种包:

  • adhoc:测试包,只有指定的设备才能下载使用,设备数量限制100。
    • developer.apple.com/account/res… 设置设备
  • appstore,这种是正式包,上架到App Store商店需要这种包。

打包命令

  • 打包为ipa文件
  • ExportOptions.plist文件中指定了打哪种包,如何获取ExportOptions.plist文件?可以先使用Xcode界面打包,生成的文件中就有ExportOptions.plist,直接使用即可。
  • --dart-define指定项目中的环境变量。在flutter项目中可以访问到,可用于区别后端的环境接口
flutter build ipa --release --export-options-plist=$PWD/ios/ExportOptions.plist --dart-define=APP_ENV=dev  

Android打包

主要介绍两种包:

  • apk包:大部分应用商店都使用这个包。且android手机可以直接安装这个包。
  • aab包:上线谷歌商店,需要打这种包

打包命令

  • --dart-define指定项目中的变量,一般用于环境变量配置
// 打包为aab包
flutter build appbundle --dart-define=APP_ENV=dev
// 打包为apk包
flutter build apk --dart-define=APP_ENV=dev

shell脚本

  • 项目中每次打包要执行多条命令,这样是否麻烦,编写shell脚本简化打包
  • 注:window电脑需要在git bash中运行shell脚本
bash build.sh  // 执行

build.sh

#!/bin/bash

# 定义菜单选项
env_options=("测试环境" "生产环境" "开发环境")
platform_options=("Android" "iOS" "All")

env_mode="test"
ios_mode="adhoc"
apk_mode="apk"

# 是否上线
read -p "是否需要上线(默认n)[y/n]: " is_release


# 不上线的包
select_platform(){
  echo -e ""
  # 选择平台
  PS3="请选择平台(默认为Android): "
  select choice in "${platform_options[@]}"; do
    case $REPLY in
      1)
        echo -e "\n平台:Android    环境:${env_mode}    是否上线:${is_release}    包类型:${apk_mode}\n"
        echo -e "开始打包: flutter build ${apk_mode} --dart-define=APP_ENV=${env_mode}"
        flutter build ${apk_mode} --dart-define=APP_ENV=${env_mode}
        break
        ;;
      2)
        echo -e "\n平台:iOS    环境:${env_mode}    是否上线:${is_release}    包类型${ios_mode}\n"
        echo -e "开始打包:flutter build ipa --release --export-options-plist=$PWD/ios/${ios_mode}.plist --dart-define=APP_ENV=${env_mode} "
        flutter build ipa --release --export-options-plist=$PWD/ios/${ios_mode}.plist --dart-define=APP_ENV=${env_mode}  
        break
        ;;
      3)
        echo -e "\n平台:iOS,Android    环境:${env_mode}    是否上线:${is_release}    包类型:${apk_mode},${ios_mode}\n"
        echo -e "开始打包Android:flutter build ${apk_mode} --dart-define=APP_ENV=${env_mode}"
        flutter build ${apk_mode} --dart-define=APP_ENV=${env_mode}
        echo -e "开始打包iOS:flutter build ipa --release --export-options-plist=$PWD/ios/${ios_mode}.plist --dart-define=APP_ENV=${env_mode}"
        flutter build ipa --release --export-options-plist=$PWD/ios/${ios_mode}.plist --dart-define=APP_ENV=${env_mode}  
        break
        ;;
      *)
        echo -e "\n平台:Android    环境:${env_mode}    是否上线:${is_release}    包类型:${apk_mode}\n"
        echo -e "开始打包:flutter build ${apk_mode} --dart-define=APP_ENV=${env_mode}"
        # flutter build ${apk_mode} --dart-define=APP_ENV=${env_mode}
        break
        ;;
    esac
  done
}
# 选择环境
select_env(){
  PS3="请选择环境(默认为测试环境): "
  select choice in "${env_options[@]}"; do
    case $REPLY in
      1)
        env_mode="test"
        break
        ;;
      2)
        env_mode="pro"
        break
        ;;
      3)
        env_mode="dev"
        break
        ;;
      *)
        env_mode="test"
        break
        ;;
    esac
  done
}


if [ "${is_release}" = "y" ]
then
  # 要上线
  env_mode="pro"
  ios_mode="appStore"
  apk_mode="appbundle"
	select_platform
else
  echo -e ""
  is_release="n"
  select_env
  select_platform
fi

效果:

玻璃钢生产厂家大型玻璃钢雕塑质量好闵行区进口玻璃钢雕塑销售价格佛山玻璃钢雕塑工艺品商场的美陈是什么意思平舆玻璃钢雕塑加工厂家楚雄市玻璃钢雕塑售价佛山发光小品玻璃钢雕塑厂家玻璃钢艺术雕塑 沈阳泉州玻璃钢卡通水果雕塑北京超市商场美陈厂家直销山东室内商场美陈供货商鲤城商场美陈制作韶关玻璃钢雕塑商家邯郸锻铜玻璃钢彩绘雕塑公司梧州玻璃钢花盆湖艺玻璃钢雕塑制品米泉玻璃钢雕塑玻璃钢孔子雕塑哪家不错南京户外玻璃钢雕塑销售厂家商场美陈人脸抽花珠海玻璃钢人物雕塑销售厂家湖南玻璃钢金属雕塑南宁玻璃钢泡沫雕塑价格山东玻璃钢仿真水果雕塑厂家东丽玻璃钢雕塑公司唐山玻璃钢雕塑厂家肇庆美陈玻璃钢卡通雕塑江宁商场美陈道具哪里有商场国庆美陈梧州玻璃钢卡通雕塑规格香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化