(原) Flutter+Dart学习 -- 入门01

原创文章,请后转载,并注明出处。

之前一直不是太想学手机端的开发,一直希望有一款实用的linux手机,但即贵且不实用,还没有保障。根本达不到商用。

个人对手机端开发是有需求的。通过手机端,可以实现很多想法。前端手机,后端Golang,世界就更大了。

禁不住诱惑,决定花点时间学习Flutter和Dart。以下是学习中的整理。接下来会有更多的学习文章。

题外话:建立了几个Demo,几个G就没有,平均每个项目的build目录占用600MB。这是编译后的apk等,删除!节约点我的SSD。

资源

Flutter 中文开发者
Flutter中文网
Flutter实战·第二版
Flutter实用插件集录
Flutter 中文文档
布局工具


对于懒鬼,每次都flutter run也是很麻烦的。修改一下flutter.bat,改名为f.bat,或者重建一个f.bat文件。

@ECHO off

SETLOCAL ENABLEDELAYEDEXPANSION

FOR %%i IN ("%~dp0..") DO SET FLUTTER_ROOT=%%~fi

REM If available, add location of bundled mingit to PATH
SET mingit_path=%FLUTTER_ROOT%\bin\mingit\cmd
IF EXIST "%mingit_path%" SET PATH=%PATH%;%mingit_path%

REM Test if Git is available on the Host
where /q git || ECHO Error: Unable to find git in your PATH. && EXIT /B 1
REM  Test if the flutter directory is a git clone, otherwise git rev-parse HEAD would fail
IF NOT EXIST "%flutter_root%\.git" (
  ECHO Error: The Flutter directory is not a clone of the GitHub project.
  ECHO        The flutter tool requires Git in order to operate properly;
  ECHO        to set up Flutter, run the following command:
  ECHO        git clone -b stable https://github.com/flutter/flutter.git
  EXIT 1
)

REM Include shared scripts in shared.bat
SET shared_bin=%FLUTTER_ROOT%\bin\internal\shared.bat
CALL "%shared_bin%"

SET flutter_tools_dir=%FLUTTER_ROOT%\packages\flutter_tools
SET cache_dir=%FLUTTER_ROOT%\bin\cache
SET snapshot_path=%cache_dir%\flutter_tools.snapshot
SET dart_sdk_path=%cache_dir%\dart-sdk
SET dart=%dart_sdk_path%\bin\dart.exe

IF "%1"=="" (
   "%dart%" --disable-dart-dev --packages="%flutter_tools_dir%\.packages" %FLUTTER_TOOL_ARGS% "%snapshot_path%" run & exit /B !ERRORLEVEL!
) ELSE (
   "%dart%" --disable-dart-dev --packages="%flutter_tools_dir%\.packages" %FLUTTER_TOOL_ARGS% "%snapshot_path%" %* & exit /B !ERRORLEVEL!
)

默认没有参数时,直接run。即我要运行flutter时,直接使用命令f即可。


学习Flutter和Dart

目前 Flutter 已经支持 iOS、Android、Web、Windows、macOS、Linux、Fuchsia
AOT (Ahead of time)即 “提前编译”
JIT(Just-in-time)即“即时编译”

安装

export PUB_HOSTED_URL=https://pub.flutter-io.cn export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

Flutter SDK下载地址:https://flutter.dev/docs/development/tools/sdk/releases
检查运行环境:flutter doctor
升级Flutter SDK和依赖包:flutter upgrade

Dart

dynamic 意思是数据类型是动态可变的,不同于var,var定义后就不能改变数据类型。
Object Dart的所有东西都继承自Object,因此Object可以定义任任何变量,且赋值后,类型也可以更改
一般可用Object代替dynamic final 常量,当类创建时才初始化。
const 编译时常量,即在编译时就初始化了。

类型类型:
List 列表
Set
Map 字典
Runes Unicode字符串

可选命名参数 {} 位置可以不一致,通过关键字传送

bool say(String msg, {String from, int color}) {
    return true;
}

调用示例: say('Hello', from:'Ease');

这里的from和color是可选参数

可选位置参数 []
可选参数必须按顺序赋值

bool say(String msg, [String from, int clock]){
    return true;
}

调用示例:
say(‘Hello’)
say(‘Hello’,‘Ease’,1) say(‘Hello’,‘Ease’) say(‘Hello’,1) // 错误

可选参数的默认值 =

bool say(String msg, {String from='ease', int color=0}){
    return true;
}

箭头语法 =>
语句后⾯只能跟⼀⾏代码,⽽且这⼀⾏代码只能⼀个表达式,⽽不能跟语句
例如:void main() => runApp(MyApp()); 等价于:

void main(){
   return runApp(MyApp());//runApp() 返回的是 void
}

操作符

/ 精确除法
~/ 整除
% 取余
++var
var++
–var
var–

条件运算符

condition ? expr1 : expr2
expr1 ?? expr2 如果expr1为null,就返回expr2的值,否则返回expr1的值。

级联操作符

..

  • StatelessWidget:无状态信息的 Widget
  • StatefulWidget:有状态信息的 Widget

就象HelloWorld一样,先看看最简Flutter程序

import 'package:flutter/material.dart';

void main() {
  runApp(const Center(
    child: Text(
      "您好!",
      textDirection: TextDirection.ltr,
    ),
  ));
}

在一个黑漆漆的背景下,一个孤零零的"您好"。
注意:如果没有textDirection属性,将显示一个出错信息:No Directionality widget found

为了更符合“现代”编程,将其“类”化

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 const Center(
      child: Text(
        "您好!",
        textDirection: TextDirection.ltr,        
      ),
    );
  }
}

继续完善,添加一个标题,定义外观样式

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(  // 标准的框架组件
      home: Scaffold(
          appBar: AppBar(title: const Text("演示")), // 设置标题
          body: const Center(
            child: Text(
              "您好!",
              textDirection: TextDirection.ltr,
              style: TextStyle(color: Color.fromARGB(255, 100, 200, 1), fontSize: 23), // 定义了颜色和字大小
            ),
          )),
      theme: ThemeData.light(),   // 组件已有几种样式
    );
  }
}

全屏应用,把右上角的DEBUG也去掉

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); // 全屏
    return MaterialApp(
      debugShowCheckedModeBanner: false, // 不显示右上角DEBUG图标
      home: Scaffold(
          appBar: AppBar(title: const Text("演示")),
          body: const Center(
            child: Text(
              "您好!",
              textDirection: TextDirection.ltr,
              style: TextStyle(color: Color.fromARGB(255, 100, 200, 1), fontSize: 23),
            ),
          )),
      theme: ThemeData.light(),
    );
  }
}

Material框架的应用

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        debugShowCheckedModeBanner: false,
        title: 'Flutter框架学习',
        home: Scaffold(
            appBar: AppBar(
              title: const Text("框架学习"),
            ),
            body: const Center(
              child: Text("框架学习内容"),
            )));
  }
}

非Material框架的应用

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: const BoxDecoration(color: Colors.white),
      child: const Center(
        child: Text(
          '非Material框架的应用',
          textDirection: TextDirection.ltr,
          style: TextStyle(
            fontSize: 17,
            color: Colors.black38,
          ),
        ),
      ),
    );
  }
}

Row: 几列 Column: 几行 Stack: Container:

mainAxisAlignment 主轴对齐方式
crossAxisAlignment 交叉轴对齐方式
mainAxisAlignment: MainAxisAlignment.spaceEvenly 空间均分

居中,通过Expanded让图片适用框架大小

Row(
  crossAxisAlignment: CrossAxisAlignment.center,
  children: [
    Expanded(
      child: Image.asset('images/pic1.jpg'),
    ),
    Expanded(
      child: Image.asset('images/pic2.jpg'),
    ),
    Expanded(
      child: Image.asset('images/pic3.jpg'),
    ),
  ],
);

Container 向组件添加边界、背景色等装饰
GridView 将组件展示为一个可滚动网格
ListView 将组件展示为一个可滚动的列表
Stack 将组件覆盖在另一个的上面


查看布局:  
import 'package:flutter/rendering.dart' show debugPaintSizeEnabled;  
debugPaintSizeEnabled = true;  
  

添加资源和图片

pubspec.yaml

flutter:
   assets:
       - assets/my_icon.png
       - assets/background.png

或者包含目录下所有

flutter:
   assets:
   - directory/
   - directory/subdirectory/

加载 assets

加载文本

加载图片

return const Image(image: AssetImage(‘graphics/background.png’));
return const AssetImage(‘icons/heart.png’, package: ‘my_icons’);

// 延时运行 Future.delayed(const Duration(milliseconds: 2000), () { … });


添加启动闪屏

  1. 在AndroidMainifest.xml中添加
  1. 在launch_background.xml中添加
    <item>
        <bitmap
            android:gravity="center"
            android:scaleType="center"
            android:src="@mipmap/ic_launcher" />
    </item>

这里就定义了启动画面为资源文件夹下的ic_launcher.png图片
注意:这里的 launch_background.xml 修改涉及到两处,一处是在drawable目录下 ,一处是在drawable-v21目录下,否则不出效果