Flutter 技术分享

2022-04-09  本文已影响0人  Mr_step

技术演进

伴随着 Android、iOS 等智能手机的不断普及,移动端已逐步取代 PC 端,成为兵家必争之地。正所谓“得移动端者得天下”,移动端已成为互联网领域最大的流量分发入口,一大批互联网公司正是在这大趋势下崛起。

为什么需要跨平台技术呢

伴随着移动互联网的高速发展,公司间竞争越来越激烈,如何将好想法快速落地、快速试错,成为备受关注的问题。提升研发效率、缩短研发周期,降低成本,让新产品新功能以最快的速度同时抵达 Android、iOS 等多端用户,成为了许多公司移动端技术的选型基础。
为了解决这一痛点。跨平台技术便应运而生,各大互联网公司为此都投入大量人力,于是出现了各种跨平台技术框架,面对移动领域的跨平台技术方案的层出不穷,又该如何做技术选型呢。

跨平台的技术划分

跨平台技术大致可分为三大类 :
1.依赖于Webview,性能体验差 如Cordova,小程序
2.使用JavaScript作为编程语言,通过中间层转化为原生控件来渲染UI界面,比如React Native、Weex。
3.自行实现一套渲染框架,可通过调用skia等方式完成自渲染,而不依赖于原生控件,如Flutter、Unity

技术选型

我认为的移动端的跨端技术方案,关注的分为4个方面:
1.研发效率:
代码复用,减少多端差异的适配工作量,降低开发成本,专注业务开发 。这也是跨平台技术会出现的重要原因之一
2.多端一致性,
很多产品在多端UI设计上,往往是整体风格统一,所以业务方采用原生各自独立开发完成后,还需额外花不少时间来修改UI以保证多端一致性。
3.性能体验
一般跨端技术方案拥有以上两种优势,但在性能方面比原生流畅更差些。如果哪天跨平台技术能和Native一样流畅,那么Native 可能就要退出历史舞台了。

Flutter技术优势

Flutter是彻底的跨平台方案,既没有采用webView,也没有采用JS桥接原生控件,而是自行实现一套UI框架,在引擎底层通过Skia渲染到屏幕。对于UI之外所需要使用的移动设备自身提供的服务,比如相机、定位、屏幕触摸等,则采用Platform Channels跟原生系统通信的方式来实现

1.高效率
采用dart语言编写代码,一套代码适用多个平台(Android、iOS、Web),以及高效的Hot Reload能快速辅助调试。

2.高一致性:实现UI像素级的控制,Flutter渲染引擎依靠跨平台Skia图形库来实现,仅依赖系统图形绘制相关的接口

3.高性能 image.png

通过一张渲染原理来看Flutter的性能优势,作为谷歌亲儿子,Flutter框架调用Dart Framework层,再直接调用到skia来渲染界面,并没有经过原生Framework过程,可见其渲染性能并不会弱于Native技术,这是一个性能上限很高的跨平台技术。

当然,不得不说目前的Flutter确实不够尽善尽美,会存在一些不够尽善尽美之处,比如生态不够健全,包体积问题,还有大天朝的网络问题,但其该方案的上限比较高,想象空间比较大,相信更多开发者参与进来,经过更多打磨,未来会做得更好

Flutter未来趋势

目前Flutter主要在Android/iOS/Web/Windows跨端,后续可能会嵌入式设备。Fuchsia是Google内部正在开发的一款新的操作系统,采用Flutter作为系统默认的UI框架,也就是说Flutter天然支持Fuchsia,这无疑让Flutter在众多的跨平台方案更有优势.

小🌰:
滑动列表的实现

import 'dart:math' as math;

import 'package:flutter/material.dart';
import 'package:flutter/material.dart' as W;
import 'package:flutter/rendering.dart';

class SliverListDemoPage extends StatefulWidget {

  @override
  _SliverListDemoPageState createState() => _SliverListDemoPageState();
}

class _SliverListDemoPageState extends State<SliverListDemoPage>
    with SingleTickerProviderStateMixin {
  int listCount = 30;

  List<Widget> _sliverBuilder(BuildContext context, bool innerBoxIsScrolled) {
    return <Widget>[
      ///头部信息
      SliverPersistentHeader(
        delegate: SliverHeaderDelegate(
          maxHeight: 180,
          minHeight: 180,
          vSync: this,
          snapConfig: FloatingHeaderSnapConfiguration(
            curve: Curves.bounceInOut,
            duration: const Duration(milliseconds: 10),
          ),
          child: Container(
            color: Colors.redAccent,
          ),
        ),
      ),
      SliverOverlapAbsorber(
        handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
        sliver: SliverPersistentHeader(
          pinned: true,

          /// SliverPersistentHeaderDelegate 的实现
          delegate: SliverHeaderDelegate(
              maxHeight: 60,
              minHeight: 60,
              changeSize: true,
              vSync: this,
              snapConfig: FloatingHeaderSnapConfiguration(
                curve: Curves.bounceInOut,
                duration: const Duration(milliseconds: 10),
              ),
              builder: (BuildContext context, double shrinkOffset,
                  bool overlapsContent) {
                ///根据数值计算偏差
                var lr = 10 - shrinkOffset / 60 * 10;
                return SizedBox.expand(
                  child: Padding(
                    padding: EdgeInsets.only(
                        bottom: 10, left: lr, right: lr, top: lr),
                    child: Row(
                      mainAxisSize: MainAxisSize.max,
                      children: <Widget>[
                        Expanded(
                          child: Container(
                            alignment: Alignment.center,
                            color: Colors.orangeAccent,
                            child: TextButton(
                              onPressed: () {
                                setState(() {
                                  listCount = 30;
                                });
                              },
                              child: const Text("按键1"),
                            ),
                          ),
                        ),
                        Expanded(
                          child: Container(
                            alignment: Alignment.center,
                            color: Colors.orangeAccent,
                            child: TextButton(
                              onPressed: () {
                                setState(() {
                                  listCount = 4;
                                });
                              },
                              child: const Text("按键2"),
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                );
              }),
        ),
      ),
    ];
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("SliverListSticky"),
      ),
      body: Container(
        child: NestedScrollView(
          physics: const AlwaysScrollableScrollPhysics(),
          headerSliverBuilder: _sliverBuilder,
          body: CustomScrollView(
            slivers: [
              W.Builder(
                builder: (context) {
                  return SliverOverlapInjector(
                      handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
                          context));
                },
              ),
              SliverList(
                delegate: SliverChildBuilderDelegate(
                      (context, index) {
                    return Card(
                      child: Container(
                        height: 60,
                        padding: const EdgeInsets.only(left: 10),
                        alignment: Alignment.centerLeft,
                        child: Text("Item $index"),
                      ),
                    );
                  },
                  childCount: 100,
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

///动态头部处理
class SliverHeaderDelegate extends SliverPersistentHeaderDelegate {
  SliverHeaderDelegate(
      {required this.minHeight,
        required this.maxHeight,
        required this.snapConfig,
        required this.vSync,
        this.child,
        this.builder,
        this.changeSize = false});

  final double minHeight;
  final double maxHeight;
  final Widget? child;
  final Builder? builder;
  final bool changeSize;
  final TickerProvider vSync;
  final FloatingHeaderSnapConfiguration snapConfig;
  AnimationController? animationController;

  @override
  double get minExtent => minHeight;

  @override
  double get maxExtent => math.max(maxHeight, minHeight);

  @override
  TickerProvider get vsync => vSync;

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    if (builder != null) {
      return builder!(context, shrinkOffset, overlapsContent);
    }
    return child!;
  }

  @override
  bool shouldRebuild(SliverHeaderDelegate oldDelegate) {
    return true;
  }

  @override
  FloatingHeaderSnapConfiguration get snapConfiguration => snapConfig;
}

typedef Widget Builder(
    BuildContext context, double shrinkOffset, bool overlapsContent);

在 Flutter 里我们常见的滑动列表场景,大致是由三部分组成:
Viewport : 它提供的是一个“视窗”的作用,也就是列表所在的可视区域大小;
Scrollable :它主要通过对手势的处理来实现滑动效果 ,比如VerticalDragGestureRecognizer 和 HorizontalDragGestureRecognizerRenderSliver, : 它主要是用于在 Viewport 里面布局和渲染内容

一般情况下 Viewport 和 Scrollable 的实现都是很通用的,所以一般在 Flutter 里要实现不同的滑动列表,就是通过自定义和组合不同的 Sliver 来完成布局
ListView 使用的是 SliverFixedExtentList 或者SliverList
GridView 使用的是 SliverGrid
PageView 使用的是 SliverFillViewport
ListView 是由 Viewport+ Scrollable 和一个RenderSliver 组成,所以在ListView 里只会有一个 RenderSliver而不是多个,想使用多个 RenderSliver 需要使用 CustomScrollView

上一篇下一篇

猜你喜欢

热点阅读