flutter:快速建构App,常用三方库汇总。【涵盖各种功能和业务】

引言

想要快速构建自己的APP,需要掌握一些基础知识和技能。本篇将项目中常用的三方库做了整理汇总,可以根据自己的需求和项目特点进行选择。无论你是新手还是老手,都可以从这些推荐的三方常用库中受益。

前言

本篇列举的三方库都是小编自己项目里确切使用过的。下面从职责划分的维度,对常用三方库进行整理归纳。

欢迎评论区补充、交流学习。

App 架构

1. 屏幕适配

flutter_screenutil: flutter 屏幕适配方案,用于调整屏幕和字体大小的flutter插件,让你的UI在不同尺寸的屏幕上都能显示合理的布局!

dart 复制代码
// 用法示例:
// 初始化方式:
ScreenUtilInit(
  // 填入设计稿中的尺寸
  designSize: const Size(960, 540),
  builder: (context, widget) {
    return const HomePage();
  },
)

// 如何适配尺寸:使用.w、.h 标识宽高即可
Container(
  width: 50.w,
  height:200.h
)

虽然对代码有侵入性,但确是目前比较常用的适配方案,小编在 AndroidiOSwindows 等平台中均使用的该库进行适配。

2. 路由

fluro: 组件化开发中,路由是必不可少的成员之一,用于模块间依赖解耦。fluro 是我们早期使用的一个路由库,使用方式简单,因为它本身不支持路由返回 widget,后期我们自己内部封装了一个库替代。

dart 复制代码
/// Push a route with custom RouteSettings if you don't want to use path params
FluroRouter.appRouter.navigateTo(
  context,
  'home',
  routeSettings: RouteSettings(
    arguments: MyArgumentsDataClass('foo!'),
  ),
);

/// Extract the arguments using [BuildContext.settings.arguments] or [BuildContext.arguments] for short
var homeHandler = Handler(
  handlerFunc: (context, params) {
    final args = context.settings.arguments as MyArgumentsDataClass;

    return HomeComponent(args);
  },
);
3. 网络请求

dio: 一个强大的 HTTP 网络请求库,支持全局配置、Restful API、FormData、拦截器、 请求取消、Cookie 管理、文件上传/下载、超时、自定义适配器、转换器等。

dart 复制代码
// 用法示例:
final response = await dio.post('/test', data: {'id': 12, 'name': 'dio'});
4. 权限申请

permission_handler: 封装有 Android、iOS 应用权限操作申请,使用方式简洁。

dart 复制代码
// 用法示例:
await Permission.camera
  .onDeniedCallback(() {
    // Your code
  })
  .onGrantedCallback(() {
    // Your code
  })
  .onPermanentlyDeniedCallback(() {
    // Your code
  })
  .onRestrictedCallback(() {
    // Your code
  })
  .onLimitedCallback(() {
    // Your code
  })
  .onProvisionalCallback(() {
    // Your code
  })
  .request();
5. 状态管理

状态管理,它可以轻松地将展示层代码与业务逻辑分开,构建数据驱动的开发模式。推荐自己在用的两个状态管理库:

flutter_bloc: 个人认为链路最简洁的状态管理库,内部实现原理初学者也能看懂。

dart 复制代码
BlocBuilder<BlocA, BlocAState>(
  buildWhen: (previousState, state) {
    // return true/false to determine whether or not
    // to rebuild the widget with state
  },
  builder: (context, state) {
    // return widget here based on BlocA's state
  }
)

flutter_riverpod: 官方大力推荐的状态管理库,响应式缓存和数据板顶框架。

dart 复制代码
// 数据源
@riverpod
Future<String> boredSuggestion(BoredSuggestionRef ref) async {
  final response = await http.get(
    Uri.https('https://boredapi.com/api/activity'),
  );
  final json = jsonDecode(response.body);
  return json['activity']! as String;
}

// UI层监听数据源变动
class Home extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final boredSuggestion = ref.watch(boredSuggestionProvider);
    // Perform a switch-case on the result to handle loading/error states
    return boredSuggestion.when(
      loading: () => Text('loading'),
      error: (error, stackTrace) => Text('error: $error'),
      data: (data) => Text(data),
    );
  }
}
6. 嵌套地狱( widget )

styled_widget: 得益于 Dart2.7.0 后支持 extension 语法,该工具库用于改善 widget 的嵌套地狱问题,提高可阅读性。

dart 复制代码
// 干掉嵌套后,代码可以这样优雅
Icon(OMIcons.home, color: Colors.white)
  .padding(all: 10)
  .decorated(color: Color(0xff7AC1E7), shape: BoxShape.circle)
  .padding(all: 15)
  .decorated(color: Color(0xffE8F2F7), shape: BoxShape.circle)
  .padding(all: 20)
  .card(
    elevation: 10,
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(20),
    ),
  )
  .alignment(Alignment.center)
  .backgroundColor(Color(0xffEBECF1));
7. 实时崩溃上报

firebase_crashlytics: firebase 全家桶之一,该库用于实时上报崩溃日志,适用于 Android、iOS 平台。

具体集成方式请查阅详情页。

UI 交互

1. 图片加载(缓存)

cached_network_image: 提供缓存能力的图片显示库。

dart 复制代码
// 用法示例:
CachedNetworkImage(
        imageUrl: "http://via.placeholder.com/350x150",
        placeholder: (context, url) => CircularProgressIndicator(),
        errorWidget: (context, url, error) => Icon(Icons.error),
)
2. 上拉下拉(刷新)

关于列表的上拉加载更多,下拉刷新,我们在实战中用过两个三方库,分别是:pull_to_refresheasy_refresh

dart 复制代码
// 用法示例:
EasyRefresh(
    header: Header(
      position: IndicatorPosition.locator,
    ),
    footer: Footer(
      position: IndicatorPosition.locator,
    ),
    onRefresh: () async {
      ....
    },
    onLoad: () async {
      ....
      return IndicatorResult.noMore;
    },
    child: CustomScrollView(
      slivers: [
        SliverAppBar(),
        const HeaderLocator.sliver(),
        ...
        const FooterLocator.sliver(),
      ],
    ),
  );
3. 提示(toast)

fluttertoast: 支持 Android、iOS、web 三端。如果要在 Mac、windows 上也实现类型功能,可以使用 overlay 封装一套自己 toast 的功能库。

dart 复制代码
Fluttertoast.showToast(
      msg: "This is Center Short Toast",
      toastLength: Toast.LENGTH_SHORT,
      gravity: ToastGravity.CENTER,
      timeInSecForIosWeb: 1,
      backgroundColor: Colors.red,
      textColor: Colors.white,
      fontSize: 16.0
);
4. webview

webview_flutter: flutter 应用内嵌套加载网页,支持 js 与 dart 通信交互。

具体集成方式请查阅详情页。

5. 二维码

qr_code_scanner: 用于扫码识别二维码内容。

dart 复制代码
// 监听扫码结果
void _onQRViewCreated(QRViewController controller) {
    this.controller = controller;
    controller.scannedDataStream.listen((scanData) {
      // scanData 就是扫码结果
    });
}

// UI 
QRView (
    key: qrKey,
    onQRViewCreated: _onQRViewCreated,
)

barcode_widget: 用于将字符串、数字显示为二维码、一维码。

dart 复制代码
BarcodeWidget(
  barcode: Barcode.code128(),
  data: 'Hello Flutter',
);
6. 唤起三方应用(URL schema)

url_launcher: 支持 schema url 跳转三方应用。支持全平台。

dart 复制代码
// 用法示例:
final Uri _url = Uri.parse('https://flutter.cn');
launchUrl(_url); //会触发自动打开浏览器跳转目标页面

// 举例一下打开淘宝
_launchURL() async { 
  String url ="taobao://item.taobao.com/item.html?id=41700658839"; 
  if (await canLaunch(url)) { 
     await launch(url); 
  } else { 
  throw 'Could not launch $url'; 
  } 
}

再附上一个网上搜来的 schema集合

dart 复制代码
QQ: mqq:// 
微信: weixin:// 
京东: openapp.jdmoble:// 
淘宝: taobao:// 
美团: imeituan:// 
点评: dianping:// 
1号店: wccbyihaodian:// 
支付宝: alipay:// 
微博: sinaweibo:// 
腾讯微博: TencentWeibo:// 
weico微博: weico:// 
知乎: zhihu:// 
豆瓣fm: doubanradio:// 
网易公开课: ntesopen:// 
Chrome: googlechrome:// 
QQ浏览器: mqqbrowser:// 
uc浏览器: ucbrowser:// 
搜狗浏览器: SogouMSE:// 
百度地图: baidumap:// bdmap:// 
优酷: youku:// 
人人: renren:// 
我查查: wcc:// 
有道词典: yddictproapp:// 
微盘: sinavdisk:// 
名片全能王: camcard://
7. 文本大小自适应

auto_size_text: Flutter小部件,可根据父控件的宽高约束,自动调整文本大小,使其完全适合其边界。

dart 复制代码
SizeBox(
  height: 100,
  child: AutoSizeText(
    'The text to display',
    style: TextStyle(fontSize: 20),
  )
)
8. 可见性监听(widget)

visibility_detector: 可监听 widget 可见度(显示的百分比),当百分比为0时,即完全不可见。

dart 复制代码
@override
Widget build(BuildContext context) {
  return VisibilityDetector(
    key: Key('my-widget-key'),
    onVisibilityChanged: (visibilityInfo) {
      var visiblePercentage = visibilityInfo.visibleFraction * 100;
      debugPrint(
          'Widget ${visibilityInfo.key} is ${visiblePercentage}% visible');
    },
    child: someOtherWidget,
  );
}
9. 图片预览(支持缩放)

photo_view: 支持全平台,含单张、批量两种预览模式。

dart 复制代码
PhotoView(
   imageProvider: AssetImage("assets/large-image.jpg"),
)

数据存储

1. 本地数据库

sqflite: 支持 iOS、Android、Mac,使用 sqlite 语法进行编辑查询。 sqflite_common_ffi: 支持桌面应用,例如 windows 平台。

2. 键值对存储(sp)

shared_preferences: 支持全平台。

dart 复制代码
// Obtain shared preferences.
final SharedPreferences prefs = await SharedPreferences.getInstance();

// Save an integer value to 'counter' key.
await prefs.setInt('counter', 10);
// Save an boolean value to 'repeat' key.
await prefs.setBool('repeat', true);
// Save an double value to 'decimal' key.
await prefs.setDouble('decimal', 1.5);
// Save an String value to 'action' key.
await prefs.setString('action', 'Start');
// Save an list of strings to 'items' key.
await prefs.setStringList('items', <String>['Earth', 'Moon', 'Sun']);

数据处理

1. 唯一值(uuid)

uuid: 生成 uuid 唯一值。

dart 复制代码
var uuid = Uuid();
// Generate a v1 (time-based) id
uuid.v1(); // -> '6c84fb90-12c4-11e1-840d-7b25c5ee775a'
// Generate a v4 (random) id
uuid.v4(); // -> '110ec58a-a0f2-4ac4-8393-c866d813b8d1'
// Generate a v5 (namespace-name-sha1-based) id
uuid.v5(Uuid.NAMESPACE_URL, 'www.google.com'); // -> 'c74a196f-f19d-5ea9-bffd-a2742432fc9c'
2. 精度处理(数值计算)

decimal: 处理数值计算时精度丢失问题。

dart 复制代码
// with double
print(0.2 + 0.1); // displays 0.30000000000000004

// with decimal
print(Decimal.parse('0.2') + Decimal.parse('0.1')); // displays 0.3
3. 加解密

encrypt: aesrsa 加解密处理。

dart 复制代码
import 'package:encrypt/encrypt.dart';

void main() {
  final plainText = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit';
  final key = Key.fromUtf8('my 32 length key................');
  final iv = IV.fromLength(16);

  final encrypter = Encrypter(AES(key));

  final encrypted = encrypter.encrypt(plainText, iv: iv);
  final decrypted = encrypter.decrypt(encrypted, iv: iv);

  print(decrypted); // Lorem ipsum dolor sit amet, consectetur adipiscing elit
  print(encrypted.base64); // R4PxiU3h8YoIRqVowBXm36ZcCeNeZ4s1OvVBTfFlZRdmohQqOpPQqD1YecJeZMAop/hZ4OxqgC1WtwvX/hP9mw==
}

crypto: shamd5 加解密处理。

dart 复制代码
import 'package:crypto/crypto.dart';
import 'dart:convert'; // for the utf8.encode method

// md5 加密
String generate_MD5(String data) {
  var content = new Utf8Encoder().convert(data);
  var digest = md5.convert(content);
  // 这里其实就是 digest.toString()
  return hex.encode(digest.bytes);
}
4. base64

自带库 convert 中提供 base64 转换方法,例如将一个文件流转化为 base64 示例如下:

dart 复制代码
static Future<String> file2Base64(String path) async {
  File file = new File(path);
  List<int> bytes = await file.readAsBytes();
  return base64Encode(bytes);
}

设备信息

1. 获取ip

r_get_ip: 获取设备当前网络环境分配的ip地址。

dart 复制代码
RGetIp.internalIP
2. 包信息

package_info_plus: 支持全平台,支持获取应用名称、包名、应用版本号、build版本号等。

dart 复制代码
import 'package:package_info_plus/package_info_plus.dart';

...

// Be sure to add this line if `PackageInfo.fromPlatform()` is called before runApp()
WidgetsFlutterBinding.ensureInitialized();

...

PackageInfo packageInfo = await PackageInfo.fromPlatform();

String appName = packageInfo.appName;
String packageName = packageInfo.packageName;
String version = packageInfo.version;
String buildNumber = packageInfo.buildNumber;
3. 设备信息

device_info_plus: 支持全平台,支持获取相关设备信息,例如 deviceId等。

dart 复制代码
import 'package:device_info_plus/device_info_plus.dart';

final deviceInfoPlugin = DeviceInfoPlugin();
final deviceInfo = await deviceInfoPlugin.deviceInfo;
final allInfo = deviceInfo.data;

JSON 数据处理

1. 序列化

自带库 convert 中提供 json 序列化、反序列化能力,如下:

dart 复制代码
// map 转 string
json.encode(mapObj)

// string 转 map
json.decode(string)
2. 模型解析

对应 json 字符串转数据模型,网上用的最多的是 json_serializable,在使用过程中发现其对不存在的空字段解析时会报错,所以我们封装了一套自己的 json 解析库,smart_dart_json (已开源)

dart 复制代码
import 'package:smart_dart_json/smart_dart_json.dart';

void main() {
  const json =
      '{"title": "示例title",  "data": [{    "name": "rex",    "age": 10  }] }';
  final sJson = SDartJson(json);
  final title = sJson['title'].stringValue;
  final students = sJson['data']
      .arrayValue
      .map(
        (e) => Student.fromJson(e), //json 转 模型
      )
      .toList();

  //模拟未定义的字段获取 (方式一)
  final undefinedKey1 = sJson['undefinedKey1'].stringValue;
  //模拟未定义的字段获取 (方式二)
  final undefinedKey2 = sJson['undefinedKey2'].string;

  print('title : $title');
  print('undefinedKey1 : $undefinedKey1'); // 打印结果为:""
  print('undefinedKey2 : $undefinedKey2'); // 打印结果为 null
}

class Student {
  final String name;
  final int age;

  Student({
    required this.name,
    required this.age,
  });

  factory Student.fromJson(SDartJson sJson) {
    return Student(
      name: sJson['name'].stringValue,
      age: sJson['age'].intValue,
    );
  }

  Map<String, dynamic> toJson() {
    return <String, dynamic>{}
      ..['name'] = name
      ..['age'] = age;
  }
}

文件相关

1. PDF预览

advance_pdf_viewer2: PDF 预览,支持 Android、iOS。

dart 复制代码
// Load from assets
PDFDocument doc = await PDFDocument.fromAsset('assets/test.pdf');
 
// Load from URL
PDFDocument doc = await PDFDocument.fromURL('http://www.africau.edu/images/default/sample.pdf');

// Load from file
File file  = File('...');
PDFDocument doc = await PDFDocument.fromFile(file);
dart 复制代码
@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Example'),
      ),
      body: Center(
          child: _isLoading
              ? Center(child: CircularProgressIndicator())
              : PDFViewer(document: document)),
    );
  }
2. 保存到相册

image_gallery_saver: 支持 Android、iOS。

dart 复制代码
// 用法示例

// 下载网络图片
var response = await Dio().get(url, options: Options(responseType: ResponseType.bytes));

// 获取网络图片名称
String name = url.substring(url.lastIndexOf("/") + 1, url.length);

// 保存到相册
final result = await ImageGallerySaver.saveImage(
    Uint8List.fromList(response.data),
    quality: 60,
    name: name,
);
3. 拍照、相册选择

image_picker

dart 复制代码
// 拍照
final _picker = ImagePicker();
final imageFile = await _picker.getImage(
  source: ImageSource.camera,
  maxHeight: 1920,
  maxWidth: 1080,
);
dart 复制代码
// 从相册选择
List<AssetEntity>? assets = await AssetPicker.pickAssets(
  context,
  pickerConfig: AssetPickerConfig(
    themeColor: Theme.of(context).primaryColor,
    requestType: RequestType.image,
    maxAssets:  9,
  ),
);
4. 文件选择(支持多文件)

file_picker

dart 复制代码
// 指定可选文件后缀
final checkedFiles = await FilePicker.platform.pickFiles(
  allowMultiple: true,
  type: FileType.custom,
  allowedExtensions: ['jpg','pdf'],
);

// 不指定文件类型,允许多选
final data = await FilePicker.platform.pickFiles(allowMultiple: true);
5. 获取系统文件路径

path_provider: 一个用于查找文件系统中常用位置的Flutter插件。支持Android, iOS, Linux, macOS和Windows。并非所有平台都支持所有方法。

dart 复制代码
final Directory tempDir = await getTemporaryDirectory();

final Directory appDocumentsDir = await getApplicationDocumentsDirectory();

final Directory? downloadsDir = await getDownloadsDirectory();

特殊需求

1. 双屏应用(一般用于收银设备)

flutter_subscreen_plugin: 支持双屏安卓设备,主副屏使用Flutter进行绘制,提供主副屏通信交互能力。

dart 复制代码
// 入口标识主副屏对应的 widget
void main() {
  var defaultRouteName = window.defaultRouteName;
  if ("subMain" == defaultRouteName) {
    viceScreenMain(); 
  } else {
    defaultMain();
  }
}

//主屏ui
void defaultMain() {
  runApp(MainApp());
}

//副屏ui
void viceScreenMain() {
  runApp(SubApp());
}
dart 复制代码
// 主屏发送数据给副屏
SubScreenPlugin.sendMsgToViceScreen("data", params: {"params": "123"});
2. HTML解析

flutter_html: 一个Flutter小部件,用于将HTML和CSS呈现为Flutter小部件。

dart 复制代码
import 'package:html/parser.dart' as htmlparser;
import 'package:html/dom.dart' as dom;
...
String htmlData = """<div>
  <h1>Demo Page</h1>
  <p>This is a fantastic product that you should buy!</p>
  <h3>Features</h3>
  <ul>
    <li>It actually works</li>
    <li>It exists</li>
    <li>It doesn't cost much!</li>
  </ul>
  <!--You can pretty much put any html in here!-->
</div>""";
dom.Document document = htmlparser.parse(htmlData);
/// sanitize or query document here
Widget html = Html(
  document: document,
);
3. Windows常用API

win32: 使用FFI封装了一些最常见的Win32 API调用,使它们可以在不需要C编译器或Windows SDK的情况下被Dart代码访问。

功能提供列表可查看:链接

4. 文字播报

flutter_tts:

dart 复制代码
await flutterTts.speak("Hello World");
5. 播放本地音频

just_audio

dart 复制代码
import 'package:just_audio/just_audio.dart';

final player = AudioPlayer();                   // Create a player
final duration = await player.setUrl(           // Load a URL
    'https://foo.com/bar.mp3');                 // Schemes: (https: | file: | asset: )
player.play();                                  // Play without waiting for completion
await player.play();                            // Play while waiting for completion
await player.pause();                           // Pause but remain ready to play
await player.seek(Duration(second: 10));        // Jump to the 10 second position
await player.setSpeed(2.0);                     // Twice as fast
await player.setVolume(0.5);                    // Half as loud
await player.stop();                            // Stop and free resources
6. ijk视频播放器

fijkplayer

dart 复制代码
import 'package:fijkplayer/fijkplayer.dart';
import 'package:flutter/material.dart';

class VideoScreen extends StatefulWidget {
  final String url;

  VideoScreen({@required this.url});

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

class _VideoScreenState extends State<VideoScreen> {
  final FijkPlayer player = FijkPlayer();

  _VideoScreenState();

  @override
  void initState() {
    super.initState();
    player.setDataSource(widget.url, autoPlay: true);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text("Fijkplayer Example")),
        body: Container(
          alignment: Alignment.center,
          child: FijkView(
            player: player,
          ),
        ));
  }

  @override
  void dispose() {
    super.dispose();
    player.release();
  }
}
7. 连接电子秤

flutter_weigh_serial: 提供获取电子秤数据能力,支持 Android、Windows 平台。注意:只支持串口方式连接(包括usb转串口)的称重设备。

dart 复制代码
import 'package:flutter_weigh_serial/flutter_weigh_serial.dart';

class _MyAppState extends State<MyApp> {

  late WeighSerialProvider weighSerialProvider;
  
  @override
    void initState() {
      super.initState();
      weighSerialProvider = WeighSerialProvider();
    }

  @override
    void dispose() {
      weighSerialProvider.close();  //退出页面时断开连接
      super.dispose();
    }

  ...
  // 连接称重设备的方法如下:
  void _connectWeigh() {
    weighSerialProvider.findAndConnect().then(
      (success) {
        if (success) {
          //搜索到称重设备并连接成功
          weighSerialProvider.weighListener?.listen(
            (data) {
              // 获取到称重数据,返回数据模型 *WeighResult*
              log('称重数据 - ${data.toMap().toString()}');
            },
          );
        } else {
          Fluttertoast.showToast(msg: '称重设备连接失败');
        }
      },
      onError: (e) {
        Fluttertoast.showToast(msg: '称重设备连接失败(${e.toString()})');
      },
    );
  }
}
8. 小票标签打印

flutter_printer_plus: flutter 端 【小票、标签】打印能力实现,支持 USB、网口,支持 Android、Windows 平台。

使用 widget 开发票据样式,直接将 flutter widget 转图像数据进行打印,支持传输方式:usb连接(支持Android、Windows)、网络连接(各平台通用)。

使用方式可点击查阅:example

9. 扫码监听

scan_gun: 实现扫码枪获取数据源,禁止系统键盘弹窗(不会触发键盘唤起,不会触发中文乱码)

dart 复制代码
// 提供 `ScanMonitorWidget` 作为父节点,嵌套使用:
ScanMonitorWidget({
    Key? key,
    required ChildBuilder childBuilder, //自己的 widget
    FocusNode? scanNode,
    FocusNode? textFiledNode,
    required void Function(String) onSubmit, //监听到的扫码枪内容
  })
10. 桌面应用窗口大小可调整

window_manager: 这个插件允许Flutter桌面应用程序调整大小和重新定位窗口。提供监听键盘快捷键组合、功能键点击事件能力。

dart 复制代码
import 'package:flutter/material.dart';
import 'package:window_manager/window_manager.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // Must add this line.
  await windowManager.ensureInitialized();

  WindowOptions windowOptions = WindowOptions(
    size: Size(800, 600),
    center: true,
    backgroundColor: Colors.transparent,
    skipTaskbar: false,
    titleBarStyle: TitleBarStyle.hidden,
  );
  windowManager.waitUntilReadyToShow(windowOptions, () async {
    await windowManager.show();
    await windowManager.focus();
  });

  runApp(MyApp());
}
11. 地图定位

amap_flutter_location: 高德定位Flutter插件。

使用方式可点击预览:链接

12. 手写签名

signature: 一个Flutter插件,提供性能优化的签名画布,能够设置自定义样式,边界和初始状态。支持所有平台。

dart 复制代码
// IMPORT PACKAGE
import 'package:signature/signature.dart';

// Initialise a controller. It will contains signature points, stroke width and pen color.
// It will allow you to interact with the widget
final SignatureController _controller = SignatureController(
    penStrokeWidth: 5,
    penColor: Colors.red,
    exportBackgroundColor: Colors.blue,
);

// INITIALIZE. RESULT IS A WIDGET, SO IT CAN BE DIRECTLY USED IN BUILD METHOD 
var _signatureCanvas = Signature(
  controller: _controller,
  width: 300,
  height: 300,
  backgroundColor: Colors.lightBlueAccent,
);

// CLEAR CANVAS
_controller.clear();

// EXPORT BYTES AS PNG
// The exported image will be limited to the drawn area
_controller.toPngBytes();

// isEmpty/isNotEmpty CAN BE USED TO CHECK IF SIGNATURE HAS BEEN PROVIDED
_controller.isNotEmpty; //true if signature has been provided
_controller.isEmpty; //true if signature has NOT been provided

// EXPORT POINTS (2D POINTS ROUGHLY REPRESENTING WHAT IS VISIBLE ON CANVAS)
var exportedPoints = _controller.points;

//EXPORTED POINTS CAN BE USED TO INITIALIZE PREVIOUS CONTROLLER
final SignatureController _controller = SignatureController(points: exportedPoints);

//DONT FORGET TO DISPOSE IT IN THE `dispose()` METHOD OF STATEFUL WIDGETS
@override
void dispose() {
  _controller.dispose();
  super.dispose();
}
相关推荐
熊的猫4 分钟前
webpack 核心模块 — loader & plugins
前端·javascript·chrome·webpack·前端框架·node.js·ecmascript
速盾cdn11 分钟前
速盾:vue的cdn是干嘛的?
服务器·前端·网络
四喜花露水44 分钟前
Vue 自定义icon组件封装SVG图标
前端·javascript·vue.js
前端Hardy1 小时前
HTML&CSS: 实现可爱的冰墩墩
前端·javascript·css·html·css3
Zender Han1 小时前
Flutter自定义矩形进度条实现详解
android·flutter·ios
hello world smile1 小时前
关于Flutter空安全升级方案整理
flutter·移动端
web Rookie1 小时前
JS类型检测大全:从零基础到高级应用
开发语言·前端·javascript
Au_ust2 小时前
css:基础
前端·css
帅帅哥的兜兜2 小时前
css基础:底部固定,导航栏浮动在顶部
前端·css·css3
yi碗汤园2 小时前
【一文了解】C#基础-集合
开发语言·前端·unity·c#