Flutter -- 进阶Packages

Posted Kevin-Dev

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter -- 进阶Packages相关的知识,希望对你有一定的参考价值。

文章目录

1. 简介

用过Flutter的开发者都知道,Flutter的库是以包(package)的方式来管理,使用package可以创建可轻松共享的模块化代码。一个最小的package包括:

  • 一个pubspec.yaml文件:声明了package的名称、版本、作者等的元数据文件
  • 一个lib文件夹:包括包中公开的(public)代码,最少应有一个<package-name>.dart文件

2. Package 类型

Packages可以包含多种内容:

  • Dart包(library package):其中包含一些Flutter特定功能,因此对Flutter框架具有依赖性,仅将用于Flutter,例如Fluro包,也就是我们平常说的Flutter包。
  • 插件包(plugin package):当我们说Flutter插件的时候就是指这,一种专用的Dart包,其中包含用Dart代码编写的API,以及针对android(使用Java或者Kotlin)和/或针对ios(使用Object-C或者Swift)平台的特定实现,一个具体例子就是battery插件包。

3. 包的使用

我们在平时中经常使用库,流程是在pubspec.yaml里声明一个依赖:

  path_provider: ^0.4.1
  cached_network_image: ^0.5.0+1

这里简单说明一下,之前没有讲解,后来查了一下,^x.x.x 这个是库对应的版本号,^0.4.1表示和0.4.1版本兼容,也可以指定特定的版本:

  • 0.4.1:特定的版本
  • any:任意版本
  • <0.4.1:小于0.4.4的版本
  • >0.4.1:大于0.4.1的版本
  • <=0.4.1:小于等于0.4.1的版本
  • >=0.4.1:大于等于0.4.1的版本
  • >=0.4.1<=0.5.0:在0.4.1和0.5.0版本之间(包含0.4.1和0.5.0),也可以用<,>

当添加依赖,使用时把相关的包导入就可使用,就好像导入dio库:

import 'package:dio/dio.dart';

就可以使用它里面提供的API:

dio_get() async
  try
      Response response;
      response = await Dio().get("http://gank.io/api/data/福利/10/1");
      if(response.statusCode == 200)
        print(response);
      else
        print("error");
      
  catch(e)
     print(e);

  


4. 开发插件包(plugin package)

下面就简单实现一个Toast 的插件包:

  1. 选择Flie > New > New FLutter Project
  2. 在目录面板中选择第二个Flutter Plugin,点击nextAndroid stdio 会有显示Select "plugin" when exposing an Android or iOS API for develops
  3. Project name填写knight_toast_plugin,这个名字随意,但是要防止和pub上的库名字冲突

看看项目的目录:

主要看四个目录就可以了:

  • android:插件包API的Android端实现
  • example:一个依赖该插件的Flutter应用程序,来说明如何使用它
  • ios:插件包API的iOS端实现
  • lib:Dart包的API,插件的客户端会使用这里实现的接口

项目创建就是一个完整的简单插件例子,这个例子是实现了platformVersion。把android目录打开:

/** KnightToastPlugin */
public class KnightToastPlugin implements MethodCallHandler 
  /** Plugin registration. */
  public static void registerWith(Registrar registrar) 
    final MethodChannel channel = new MethodChannel(registrar.messenger(), "knight_toast_plugin");
    channel.setMethodCallHandler(new KnightToastPlugin());
  

  @Override
  public void onMethodCall(MethodCall call, Result result) 
    if (call.method.equals("getPlatformVersion")) 
      result.success("Android " + android.os.Build.VERSION.RELEASE);
     else 
      result.notImplemented();
    
  

发现和一开始使用平台通道编写平台特定的代码很像,从上面知道knightToastPlugin这个插件实现了MethodCallHandler,先看看这个MethodCallHandler接口:

    //返回结果接口
    public interface Result 
        //成功
        void success(@Nullable Object var1);
        //失败
        void error(String var1, @Nullable String var2, @Nullable Object var3);
        //没有实现接口时回调 通常是调用了未知的方方法
        void notImplemented();
    
    //处理本地方法的请求接口
    public interface MethodCallHandler 
        void onMethodCall(MethodCall var1, MethodChannel.Result var2);
    

反正实现一个插件时需要实现这个接口,下面实现弹出吐司这个功能:

4.1 实现MethodCallHandler接口

public class KnightToastPlugin implements MethodCallHandler

  //插件注册
  public static void registerWith(Registrar registrar)
    //samples.flutter/knight_toast_plugin 这是Method channel的名字 上面是有说过,这里并且添加了域名,为了防止冲突
    final MethodChannel channel = new MethodChannel(registrar.messenger(), "samples.flutter/knight_toast_plugin");
    channel.setMethodCallHandler(new KnightToastPlugin());

  

  @Override
  public void onMethodCall(MethodCall methodCall, Result result) 

  

因为使用过Toast都知道,Android需要一个上下文环境(Context),把Context参数加上:

  private Context mContext;
  public KnightToastPlugin(Context mContext)
    this.mContext = mContext;
  
  //插件注册
  public static void registerWith(Registrar registrar)
    ....
    //从Registrar获得context
    channel.setMethodCallHandler(new KnightToastPlugin(registrar.context()));

  

4.2 完善onMethodCall方法

  @Override
  public void onMethodCall(MethodCall methodCall, Result result) 
     //首先判断方法名是否为"showToast"
     if(methodCall.method.equals("showToast"))
        //因为调用原生,只能传递一个参数,如果想要传递多个,那就放在map里,用map传递
        //用MethodCall.argument("xxxx")来取值
        //显示内容
        String message = methodCall.argument("message");
        //时间为short 还是 long
        String duration = methodCall.argument("duration");
        //调用原生弹出吐司
        Toast.makeText(mContext,message,duration.equals("length_short") ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG).show;
        //成功
        result.success(true); 
      else 
        //没这个方法
        result.notImplemented(); 
     
  

4.3 Flutter客户端

在 FLutter 客户端需要做有两步:

  • 生成一个MethodChannel,例子已经帮生成了。
  • 通过这个MethodChannel调用showToast方法。

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


enum Duration
  length_short,
  length_long


class KnightToastPlugin 
  //这里要和你在android目录下写的插件通道要对应 new MethodChannel(registrar.messenger(), "samples.flutter/knight_toast_plugin");
  static const MethodChannel _channel =
      const MethodChannel('samples.flutter/knight_toast_plugin');
//  不需要自带的例子
//  static Future<String> get platformVersion async 
//    final String version = await _channel.invokeMethod('getPlatformVersion');
//    return version;
//  
  static Future<bool> showToast(String message,Duration duration) async
    //参数封装
    var argument = 'message':message,'duration':duration.toString();
    //这个方法是异步调用 "showToast"对应在上面所写的原生代码的methodCall.method.equals("showToast")
    var success = await _channel.invokeMethod('showToast',argument);
    return success;
  


4.4 使用插件

example > lib目录下的main.dart修改如下:

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

void main() => runApp(MyApp());

class MyApp extends StatefulWidget 
  @override
  _MyAppState createState() => _MyAppState();


class _MyAppState extends State<MyApp> 
  _showToast()
    KnightToastPlugin.showToast("吐司出来~", Duration.length_short);
  

  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Center(
          child: Text('吐司例子'),
        ),
        floatingActionButton : FloatingActionButton(
            onPressed: _showToast,
            tooltip: "可以弹出toast",
            child: new Icon(Icons.audiotrack)
        ),
      ),
    );
  

效果如下:

4.5 发布插件

插件功能做出来,下面就等发布了,下面把插件发布到pub.dartlang.org上,发布需要科学上网。。,检查pubspec.yaml,这里需要补一下基本信息:

name: knight_toast_plugin ->插件名字

description: toast_plugin ->插件描述

version: 0.0.1 ->插件版本

author: kevin0724@163.com ->作者

homepage: https://github.com/KnightAndroid->主页

建议将下面文档添加到插件包:

  • README.md:结束插件的文件
  • CHANGELOG:记录每个版本中的更改
  • LICENSE:包含插件许可条款的文件

检查插件,在根目录执行下面命令,检测插件有没有问题:

flutter packages pub publish --dry-run

如果显示包太大,就把build、.idea删除,并且把一些警告解决,最后输出:

Package has 0 warnings.

下面就可以真正发布插件了,命令如下:

flutter packages pub publish

以上是关于Flutter -- 进阶Packages的主要内容,如果未能解决你的问题,请参考以下文章

flutter Dart Packages包导入报错:Target of URI doesn‘t exist ‘package:xxxxx‘ 解决方法

Flutter VSCode 智能感知不适用于单文件包

Flutter - 自动引用pub.dartlang.org/packages上最新的packages

Flutter 用户应该运行 `flutter packages get` 而不是 `pub get`

Flutter开发 Flutter 包和插件 ( 开发 Dart 插件包 | 发布 Dart 插件包 )

Flutter 动态更改应用程序启动图标