Flutter系列一:Flutter快速入门

803 阅读4分钟

Flutter快速入门

前言

Flutter安装及Hello World请看官方文档,本教程不做介绍

Flutter与其它移动开发框架

原生开发框架工作原理图

基于Webview框架的工作原理图

RN工作原理图(严重依赖OEM组件)

Flutter工作原理图

Flutter与其它方案对比

  • Flutter与JS解决方案一样,使用AOT(提前编译),而不是JIT(实时编译)
  • 与其它方案相比,Flutter性能要好得多
  • Flutter消除了Bridge,且不依赖于OEM平台
  • Flutter允许自定义组件使用屏幕上的所有像素,即使用Flutter开发的应用程序在安卓和iOS的每个版本上都显示相同的内容
  • Flutter使用Widgets Rendering来处理画布(canvas)和事件(events),使用Platform Channels来使用服务(位置、蓝牙等)
  • Flutter Hot Reload使用了Dart的JIT编译特性

Flutter的四种调试方式

Dart analyzer

可以创建analysis_options.yaml文件,指定配置,有助于编写更好的Flutter代码

Dart observatory

可以通过添加断点或者debugger()语句,中断应用执行进行调试,更多说明

Visual debugging

  • 启用debugPaintSize选项,在入口文件处,指明debugPaintSizeEnabledtrue,代码及效果如下:
void main() {
    debugPaintSizeEnabled=true;
    runApp(MyApp());
}

  • 类似的还有启用debugShowMaterialGrid,进行MaterialApp的网格调试
  • 启用showPerformanceOverlay,将展示性能图,性能图分为上下两部分。上部为GPU线程花费的时间,下部为CPU线程花费的时间

Flutter widget inspector

类似于Web调试的浏览器选择工具,选择后效果如下

Flutter的Widgets

说明

Flutter UI里一切皆Widgets,写过React及Styled-Components应该很容易理解,不过Flutter的部分样式也是由Widget组成,例如:Padding、Center等

常用Widgets列表

Container

使用示例

Center(
  child: Container(
    height: 200.0,
    width: 200.0,
    child: Image.network("https://flutter.io/images/flutter-mark-square-100.png"),
  ),
),

Container可以用于条件表达式展示一个空组件,如

function getIcon(bool condition) {
    if (condition == true) return Icon(Icons.edit);
    else return Container();
}

Image

Text

Icon

RaisedButton

Scaffold:Scaffold用于展示drawers, snackbars, bottomsheets, floating-action buttons等,使用示例

RaisedButton(
    onPressed: () {
        Scaffold.of(context).showSnackBar(SnackBar(
            content: Text("HELLO!"),
        ));
    },
    child: Text("BUTTON"),
    color: Colors.blue,
),

Appbar

PlaceHolder

Row

Column

ListView

GestureDetector:为非按钮组件添加交互性

Stateless widget & Stateful widget

Stateless widget:无状态的小组件,示例

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

Stateful widget:有状态的小组件,示例(通过setState更改state)

class _MyHomePageState extends State<MyHomePage> {
    bool value = false;

    @override
    Widget build(BuildContext context) {
        return new Scaffold(
          backgroundColor: value ? Colors.black : Colors.white,
          appBar: new AppBar(
          title: new Text(widget.title),
        ),
        body: Center(
            child: Switch(
                value: value,
                onChanged: (v) {
                  setState(() {
                    value = v;
                  });
                }),
            ),
        );
    }
}

Flutter路由

通过Navigator.push()打开新页面,通过Navigator.pop()返回上一页,示例

onPressed() {
    Navigator.push(
        context,
        MaterialPageRoute(builder: (context) => SecondScreen()), // SecondScreen为要跳转到的页面
   );
}

注:要达到全屏模态框的效果可以将MaterialPageRoutefullscreenDialog属性设置为true

Flutter中的约束

约束给出高度与宽度的最小值与最大值 RenderBoxes有三种不同的渲染机制

  1. 试图以当期容器的最大宽高渲染,如:ListViewCenter
  2. 试图以其子组件宽高作为自身宽高渲染
  3. 试图以特定宽度渲染,如:ImageText

特殊的:当未向Container构造函数传入宽高值时,它将倾向于最大渲染,但如果指明了宽高值,则使用特定大小。 特定的约束是无界(限)约束,这种情况下最大宽高将被设置为double.INFINITY

Flutter中的网络请求

示例

import 'dart:async';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

Future<Post> fetchPost() async {
  final response =
  await http.get('http://localhost:3000/Movies/1');

  if (response.statusCode == 200) {
    return Post.fromJson(json.decode(response.body));
  } else {
    throw Exception('Failed to load post');
  }
}

class Post {
  final int id;
  final String movieName;
  final int year;
  final String category;

  Post({this.id, this.movieName, this.year, this.category});

  factory Post.fromJson(Map<String, dynamic> json) {
    return Post(
      id: json['id'],
      movieName: json['movieName'],
      year: json['year'],
      category: json['category'],
    );
  }
}

void main() => runApp(MyApp(post: fetchPost()));

class MyApp extends StatelessWidget {
  final Future<Post> post;

  MyApp({Key key, this.post}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'JSON Fetch Data Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('JSON Fetch Data Example'),
        ),
        body: Center(
          child: FutureBuilder<Post>(
            future: post,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return Text(snapshot.data.movieName);
              } else if (snapshot.hasError) {
                return Text("${snapshot.error}");
              }

              return CircularProgressIndicator();
            },
          ),
        ),
      ),
    );
  }
}

GET/POST的通用化原生封装

import 'dart:async';
import 'package:http/http.dart' as http;
import 'dart:convert';

class Api {
  static bool useHttps = false;
  static String prefix = "127.0.0.1:7001";

  static void init({String prefix, bool useHttps}) {
    Api.useHttps = useHttps;
    Api.prefix = prefix;
  }

  static Future<dynamic> get({
    String url,
    Map<String, dynamic> param = null,
    bool useDecode = true,
  }) async {
    url = (Api.useHttps ? 'https' : 'http') + '://' + prefix + url;
    if (param != null) {
      List<String> kv = [];
      for (String key in param.keys) {
        kv.add(key + '=' + param[key].toString());
      }
      url += '?' + kv.join('&');
    }
    print(url);
    var client = new http.Client();
    var resp = await client.get(url);
    if (resp.statusCode != 200) {
      return null;
    }

    if (useDecode) {
      var json = new JsonDecoder();
      return json.convert(resp.body);
    }

    return null;
  }

  static Future<dynamic> post({
    String url,
    Map<String, dynamic> param = null,
    bool useDecode = true,
  }) async {
    url = (Api.useHttps ? 'https' : 'http') + '://' + prefix + url;
    var client = new http.Client();
    var resp = await client.post(url, body: param);
    if (resp.statusCode != 200) {
      return null;
    }

    if (useDecode) {
      var json = new JsonDecoder();
      return json.convert(resp.body);
    }

    return null;
  }
}

其它

IDE技巧编写Flutter效率技巧

Android Studio

  • quickfix(快速修复): Alt + Enter

VS Code

  • quickfix(快速修复): Ctrl + .

Flutter包管理

Flutter Pub

Flutter打开webview

使用flutter官方的url_launcher

Flutter 常用命令

$ flutter packages get # 在项目内部执行,安装pubspec.yaml指明的依赖
$ flutter channel # 查看渠道列表,及当前在用的渠道
$ flutter channel dev # 切换渠道为dev
$ flutter doctor # 执行flutter环境检查
$ flutter upgrade # 升级
$ flutter run -d [device ID] # 通过该设备启动应用