flutter后端(跨平台UI框架——Flutter初识)

flutter后端(跨平台UI框架——Flutter初识)
跨平台UI框架——Flutter初识

为什么要跨平台?

以前我们将开发人员简单粗暴的分成前端后端,但现在不行了,由于种种原因,越来越多的程序员被逼成全栈,啥都得会、啥都得干。

其实软件开发领域细分工种很多,就以前端为例,不仅包含通常意义上的Web端,其实还有移动端(安卓、ios、鸿蒙等)、桌面端、嵌入式前端、小程序等等。要命的是,它们各自的技术栈都不一样,一个人几乎不可能掌握所有的开发技术。想起来一个经典段子,“你是学计算机的,来帮我修修电脑吧!”

若干年以前,为了做一款手机App,我们部门配备了一个安卓开发以及ios开发,如果是小型团队,人手不足,通常是从Web端做起,然后用H5技术打包成各个平台运行的程序,比如移动端的WebView嵌套,桌面端的Electron,这种开发方式也是存在诸多问题。

然后就出现了跨平台技术,统一技术栈,真正的一套代码多端运行。Flutter是热度较高,成熟度也较好的框架,除此之外还有Qt、React Native,以及Jetpack Compose在跨平台方面的尝试等等,当然,各自都有适用场景和优缺点。今天我们先简单认识一下Flutter。

认识Flutter

Flutter是Google开源的应用开发框架,仅通过一套代码库,就能构建精美的、原生平台编译的多平台应用。支持移动、Web、桌面和嵌入式端,对于小程序,也可以通过插件进行转换,基本涵盖了所有涉及UI的平台。

Flutter框架自下而上分为Embedder、Engine和Framework三层。

  • Embedder:操作系统适配层,实现了渲染Surface设置,线程设置,以及平台插件等平台相关特性的适配;
  • Engine:该层负责图形绘制、文字排版和提供Dart运行时,Engine层具有独立虚拟机,正是由于它的存在,Flutter程序才能运行在不同的平台上,实现跨平台运行;
  • Framework:使用Dart编写的一套基础视图库,包含了动画、图形绘制和手势识别等功能,是开发者主要接触的部分。

核心特性

跨平台一致性:

  • 采用 Dart 语言编写,通过 Skia 图形引擎直接渲染 UI;
  • 不使用系统原生控件,而是完全自己绘制 UI,因此在不同平台上能保持一致的 UI 外观和行为;

高性能:

flutter后端(跨平台UI框架——Flutter初识)

  • 采用AOT(Ahead-Of-Time)编译技术,直接编译为原生机器代码。渲染性能接近原生应用;
  • 基于事件队列的非阻塞IO模型,配合async/await语法简化异步编程,使用Isolate实现多线程计算;

热重载(Hot Reload):

  • 开发时保存代码更改后,1秒内即可看到效果;
  • 相比原生开发需要重新编译安装,效率提升显著;

丰富的组件库:

  • 提供300+预构建组件(Widgets);
  • 包含Material Design(Android风格)和Cupertino(iOS风格)两套组件;
  • 支持自定义主题和样式扩展;

核心概念

基础概念

  • Statefulwidget:有状态的Widget,可以根据用户交互或其他因素而改变。
  • StatelessWidget:无状态的Widget,一旦创建就不会改变,适用于静态内容展示,
  • Dart语言:Flutter使用Dart语言进行开发,它具有强类型和面向对象的特性。
  • MaterialApp:基于Material Design的Flutter应用的顶层Widget,包含整个应用的基本配置。
  • Scaffold:提供应用程序的基本结构,例如AppBar、Drawer和BottomNavigationBar。

布局与结构

  • Container:用于布局和装饰的基本组件,可以包含其他Widget。
  • Column:垂直排列的Widget,用于构建垂直布局。
  • Row:水平排列的Widget,用于构建水平布局。
  • ListView:可滚动的列表视图,用于展示大量数据。
  • AppBar:顶部应用栏,通常包含标题、图标和操作按钮。
  • GestureDetector:用于处理用户手势的Widget例如点击、滑动等。

功能进阶

  • Future和异步编程:Flutter中使用Future和async/await进行异步编程。
  • Navigator:用于管理页面导航的Widget,包括页面的跳转和返回。
  • PageRoute:用于定义页面切换的基本类,可自定义页面切换效果。
  • Asset lmage:加载应用内部资源中的图片。
  • Provider:状态管理库,简化数据共享和更新。
  • Theme:定义应用程序的主题,包括颜色、字体等。

状态管理

状态管理是所有UI框架的核心设计之一,我们以第一个Flutter程序——计数器为例来看一下

import 'package:flutter/material.dart';void main() => runApp(MyApp());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'),    );  }}class MyHomePage extends StatefulWidget {  final String title;  MyHomePage({Key? key, required this.title}) : super(key: key);  @override  _MyHomePageState 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>[            Text('You have pushed the button this many times:'),            Text('$_counter', style: Theme.of(context).textTheme.headline4),          ],        ),      ),      floatingActionButton: FloatingActionButton(        onPressed: _incrementCounter,        tooltip: 'Increment',        child: Icon(Icons.add),      ),    );  }}

这里包含了前面提到的很多核心概念。状态管理主要就是这个setState,它会更新关联的StatefulWidget,也就是重新执行build,这是最基本的状态管理,在例子中就表现为点击一次按钮,页面的数字就+1。

但使用不当的话,这种方式容易失控。setState会触发整个Widget子树重建,且状态无法跨页面共享。靠setState堆砌代码,往往导致页面卡顿、状态混乱、跨页面传参耦合等。

因此,setState是基础,但不能过度依赖。官方推荐的Provider是更适用大型项目的状态管理方案。看我们完善过后的计数器例子

import 'package:flutter/material.dart';import 'package:provider/provider.dart';import 'package:dio/dio.dart';/// 计数器状态模型class CounterModel with ChangeNotifier {  int _count = 0;    int get count => _count;  // 增加计数  void increment() {    _count++;    _notify();  }  // 重置状态  void reset() {    _count = 0;    _notify();  }  // 异步接口请求  Future<void> fetchData() async {    try {      final response = await Dio().get("http://test.com/api/getCount/user1");      // 请求成功,数据赋值      _count = response.data.toString();    } catch (e) {      // 请求失败    } finally {      _notify();    }  }  void _notify() {    if (mounted) {      notifyListeners();    }  }  @override  void dispose() {    super.dispose();  }}// 程序入口void main() {  runApp(    MultiProvider(      providers: [        ChangeNotifierProvider(create: (context) => CounterModel()),      ],      child: const MyApp(),    ),  );}// 页面class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: const Text("计数器")),      body: Padding(        padding: const EdgeInsets.all(16),        child: Column(          children: [            Consumer<CounterModel>(              // 仅监听count变化              builder: (context, count, child) => Text(                "计数:$count",                style: const TextStyle(fontSize: 20),              ),            ),            const SizedBox(height: 20),            ElevatedButton(              onPressed: () => context.read<CounterModel>().reset(), // 避坑:read不监听,仅获取模型              child: const Text("重置状态"),            ),          ],        ),      ),      floatingActionButton: Column(        mainAxisAlignment: MainAxisAlignment.end,        children: [          FloatingActionButton(            onPressed: () => context.read<CounterModel>().increment(),            child: const Icon(Icons.add),          ),          const SizedBox(height: 10),          FloatingActionButton(            onPressed: () => context.read<CounterModel>().fetchData(),            child: const Icon(Icons.download),          ),        ],      ),    );  }}

示例代码为了演示方便还有些粗糙,我们忽略一些细节,Provider其实就是经典的观察者模式,只会影响Consumer组件的状态,并且我们将数据和展示层分开了,如果更进一步,model中只定义数据模型,将model和view之间的通信及业务逻辑处理提取出来(Presenter),就得到了Model-View-Presenter (MVP)的开发范式。看到这里,一个能用在企业生产中的正式项目框架已初步成型。

项目结构

直接看图

pubspec.yaml就是项目的依赖管理,lib是主要写代码的目录,箭头所指的是各平台目录,这些目录是新建项目自动生成的,只需要在里面配置平台的一些编译配置即可,比如安卓的sdk等,开发基本都在lib目录下。

我们再看下lib目录里面的结构

main.dart是程序入口文件。前面例子中的main函数,基础页面框架、导航、主题等,还有一些初始化的东西,如权限申请等可以放在里面。然后可以按照模块来划分目录,比如图中的登录模块,里面包含model、page、router,还有一些公共组件widget等,以及前面提到的presenter等,可以据此来划分子目录。当然,这些没有明确的规范,这是我个人的习惯思路。

说到这里,其实Flutter的一些语法结构,包括设计思想,跟安卓原生开发的Jetpack Compose有点像,就连Dart语言跟Kotlin也有一些共通之处,不过都是Google的产品,倒也正常。

以上我们初步认识了Flutter,感兴趣或者有跨平台开发需求的可以更深入学习哈~

文章版权声明:除非注明,否则均为边学边练网络文章,版权归原作者所有