IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Flutter —— Packages 和 Plugin -> 正文阅读

[移动开发]Flutter —— Packages 和 Plugin

1. Packages

1.1 简介

Dart package 最低要求是包含一个 pubspec.yaml 文件,package只包含dart部分。此外,一个 package 可以包含依赖关系 (在 pubspec.yaml 文件里声明)、 Dart 库、应用、资源、测试、图片和例子等,开发者可以使用package 快速构建应用。

1.2 创建 Packages

这里新建一个Flutter Package 项目,可以看到这里面是没有Android和iOS的文件的。

在这里插入图片描述
将之前的 friends页面里面的index_bar的内容拷贝到test_package_demo里面,去除掉没有的import,然后将所需要的内容拷贝进来。

library test_package_demo;


import 'package:flutter/material.dart';

class IndexBar extends StatefulWidget {
  final void Function(String str) indexBarCallBack;

  const IndexBar({Key? key, required this.indexBarCallBack}) : super(key: key);

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

String getIndex(BuildContext context, Offset globalPosition) {
  //拿到当前小部件
  RenderBox box = context.findRenderObject() as RenderBox;
  //拿到y值,globalToLocal当前位置距离部件的原点的距离
  double y = box.globalToLocal(globalPosition).dy;
  // 算出字符高度
  var itemHeight = screenHeight(context) / 2 / INDEX_WORDS.length;
  // 算出第几个item
  int index = (y ~/ itemHeight).clamp(0, INDEX_WORDS.length - 1);

  return INDEX_WORDS[index];
}

class _IndexBarState extends State<IndexBar> {
  Color _bkColor = Color.fromRGBO(1, 1, 1, 0.0);
  Color _textColor = Colors.black;

  @override
  void initState() {
    // TODO: implement initState
  }

  @override
  Widget build(BuildContext context) {
    final List<Widget> words = [];

    for (int i = 0; i < INDEX_WORDS.length; i++) {
      words.add(Expanded(
        child: Text(
          INDEX_WORDS[i],
          style: TextStyle(fontSize: 10, color: _textColor),
        ),
      ));
    }

    return Positioned(
      right: 0.0,
      top: screenHeight(context) / 8,
      height: screenHeight(context) / 2,
      width: 30,
      child: GestureDetector(
          onVerticalDragDown: (DragDownDetails details) {
            setState(() {
              _bkColor = const Color.fromRGBO(1, 1, 1, 0.5);
              _textColor = Colors.white;
            });
          },
          onVerticalDragUpdate: (DragUpdateDetails details) {
            getIndex(context, details.globalPosition);
          },
          onVerticalDragEnd: (DragEndDetails details) {
            setState(() {
              _bkColor = const Color.fromRGBO(1, 1, 1, 0.0);
              _textColor = Colors.black;
            });
          },
          child: Container(
            child: Column(
              children: words,
            ),
            color: _bkColor,
          )),
    );
  }
}

//主题色
const Color weChatThemColor = Color.fromRGBO(220, 220, 220, 1.0);

//屏幕宽高
double screenWidth(BuildContext context) => MediaQuery.of(context).size.width;
double screenHeight(BuildContext context) => MediaQuery.of(context).size.height;
const INDEX_WORDS = [
  '🔍',
  '☆',
  'A',
  'B',
  'C',
  'D',
  'E',
  'F',
  'G',
  'H',
  'I',
  'J',
  'K',
  'L',
  'M',
  'N',
  'O',
  'P',
  'Q',
  'R',
  'S',
  'T',
  'U',
  'V',
  'W',
  'X',
  'Y',
  'Z'
];

这里面使用到了资源,那么就需要进行处理。在package 中添加一个Directory images,然后添加图片进去。

在这里插入图片描述
然后到pubspec.yaml 里面添加images的引用。

在这里插入图片描述
然后看到顶部这里的四个参数。

  • name:包的名称
  • description: 包的描述
  • version: 包的版本
  • homepage:包的主页,需要是可以访问的否则会扣分

在这里插入图片描述

1.3 发布 Package

接下来要发布刚才创建的Package,那么先使用终端来到Package的文件夹中。

在这里插入图片描述
首先需要使用命令检查包是否有问题.

flutter packages pub publish --dry-run 

这里看到刚才创建的包是没有问题的
在这里插入图片描述
如果把刚才四个参数中的某一个去掉那么就会报错。

在这里插入图片描述
如果检查通过了之后,那么就可以使用命令来发布Package了。

flutter packages pub publish 

发布的时候需要注意

  • 发布的时候需要科学上网
  • 需要验证谷歌账号,所以需要有一个谷歌账号。

这里来到pub.dev 然后登陆谷歌账号。
在这里插入图片描述
发布之后会会提醒我们去一个节目然后点击Allow access
在这里插入图片描述
进去之后登陆就会提示Pub Authorized Successfully。
在这里插入图片描述
这里出来之后发现还是有License 的问题,这个证书需要在github创建,是版权相关的问题。

在这里插入图片描述
在github新建项目,点击Choose a license然后选择BSD 3
在这里插入图片描述
接着就可以使用这里的License 文件了。
在这里插入图片描述
那么这个时候就可以发布了。
在这里插入图片描述
这个时候就可以在pub.dev搜索到上传的package 了。
在这里插入图片描述
也可以在My packages 里面看到。
在这里插入图片描述

1.4 Package在项目中应用

来到之前的项目中来使用这个package。
在pubspec.yaml 里面的dependencies添加刚才的package。
在这里插入图片描述
可以在External libraries的 dark packages 里面看到Package被成功下载下来了。
在这里插入图片描述
接着来到friends_page中使用,为了不冲突使用as来取别名。

import 'package:test_package_demo/test_package_demo.dart' as ls;

这时候就可以使用包中的IndexBar了。

在这里插入图片描述
这里其实还有两个问题

  • 图片问题
  • 包里只有dart文件

这里如果要把图片也加入到包里面,那么就将包里面的images拖到lib里面。

在这里插入图片描述
然后在使用图片的时候指定包名,这样就可以解决图片问题了。
在这里插入图片描述
改完了之后,在pubspec.yaml里面修改一下version,重新发布一下Package,然后到项目中修改一下包版本。
在这里插入图片描述
还需要在assets里面添加图片的引用,这里需要是完整的路径。

 - packages/test_package_demo/images/bubble.png

这样上面的问题就解决了,但是可以看到引用图片的话就非常的麻烦,所以需要对包进行修改。在包里面的IndexBar给一个参数让外界可以传图片进来。

  final void Function(String str) indexBarCallBack;
  final ImageProvider? image;
  final Icon? icon;

  const IndexBar(
      {Key? key, required this.indexBarCallBack,  this.image, this.icon})
      : super(key: key);

然后在图片那根据传进来的参数进行判断显示。

 widget.icon ??
                          Image(
                            image: widget.image ??
                                const AssetImage('images/bubble.png',
                                    package: "test_package_demo"),
                            width: 60,
                          ),

1.5 Package分数

Package的分数对于一个包是很重要的,如果分数过低,那么基本就不会有人用这个包。对于新创建的包来说,一般只需要注意2个点

  • description的长度 (60 - 180个字)
  • 提供example
    在这里插入图片描述
    描述都会写,这里来创建一个示例工程。
    新建一个项目,然后到pubspec.yaml里面添加包。
    在这里插入图片描述
    在工程里面代码
import 'package:flutter/material.dart';
import 'package:test_package_demo/test_package_demo.dart' as ls;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home:  FriendsPage(),
    );
  }
}



class _FriendCell extends StatelessWidget {
  const _FriendCell({this.imageUrl, this.name, this.groupTitle, this.imageAssets});

  final String? imageUrl;
  final String? name;
  final String? groupTitle;
  final String? imageAssets;
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Container(
          alignment: Alignment.centerLeft,
          padding: EdgeInsets.only(left: 10),
          height: groupTitle != null ? 30 : 0,
          color: ls.weChatThemColor,
          child: groupTitle != null
              ? Text(
            groupTitle!,
            style: TextStyle(color: Colors.grey),
          )
              : null,
        ), //头部
        Container(
          color: Colors.white,
          child: Row(
            children: [
              Container(
                margin: EdgeInsets.all(10),
                width: 34,
                height: 34,
                decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(6.0),
                    image: DecorationImage(
                      image: imageUrl != null
                          ? NetworkImage(imageUrl!)
                          : AssetImage(imageAssets!,package: "test_package_demo") as ImageProvider,
                    )),
              ), //图片
              Container(
                // color: Colors.red,
                width: ls.screenWidth(context) - 54,
                child: Column(
                  children: [
                    Container(
                      alignment: Alignment.centerLeft,
                      height: 54,
                      child: Text(
                        name!,
                        style: TextStyle(fontSize: 18),
                      ),
                    ),
                    Container(
                      height: 0.5,
                      color: ls.weChatThemColor,
                    ), //下划线
                  ],
                ),
              ), //昵称+下划线
            ],
          ),
        ), //Cell的内容
      ],
    );
  }
}

class FriendsPage extends StatefulWidget {
  @override
  _FriendsPageState createState() => _FriendsPageState();
}

class _FriendsPageState extends State<FriendsPage>
    with AutomaticKeepAliveClientMixin<FriendsPage> {
  @override
  // TODO: implement wantKeepAlive
  bool get wantKeepAlive => true;

  double _cellHeight = 54.5;
  double _groupHeight = 30.0;

  //字典,里面放item和高度的对应的数据。
  final Map _groupOffsetMap = {
    ls.INDEX_WORDS[0]: 0.0,
    ls.INDEX_WORDS[1]: 0.0,
  };

  final List<Friends> _headerData = [
    Friends(imageAssets: 'images/新的朋友.png', name: '新的朋友'),
    Friends(imageAssets: 'images/群聊.png', name: '群聊'),
    Friends(imageAssets: 'images/标签.png', name: '标签'),
    Friends(imageAssets: 'images/公众号.png', name: '公众号'),
  ];
  final List<Friends> _listDatas = [];
  late ScrollController _scrollController;
  @override
  void initState() {
    super.initState();

    _scrollController = ScrollController();
    //创建数据
    _listDatas
      ..addAll(datas)
      ..addAll(datas);
    //排序
    _listDatas.sort((Friends a, Friends b) {
      return a.indexLetter!.compareTo(b.indexLetter!);
    });
    var _groupOffset = _cellHeight * _headerData.length;
    //进过循环计算,将每一个头的位置算出来。放入字典
    for (int i = 0; i < _listDatas.length; i++) {
      if (i < 1) {
        //第一个cell一定有头!
        _groupOffsetMap.addAll({_listDatas[i].indexLetter: _groupOffset});
        //保存完了再加_groupOffset
        _groupOffset += _cellHeight + _groupHeight;
      } else if (_listDatas[i].indexLetter == _listDatas[i - 1].indexLetter) {
        //不同存,只需要加Cell的高度
        _groupOffset += _cellHeight;
      } else {
        _groupOffsetMap.addAll({_listDatas[i].indexLetter: _groupOffset});
        //保存完了再加_groupOffset
        _groupOffset += _cellHeight + _groupHeight;
      }
    }
  }

  Widget _itemForRow(BuildContext context, int index) {
    //显示头部4个Cell
    if (index < _headerData.length) {
      return _FriendCell(
        imageAssets: _headerData[index].imageAssets,
        name: _headerData[index].name,
      );
    }
    //是否显示组名字!
    bool _hiddenIndexLetter = (index - 4 > 0 &&
        _listDatas[index - 4].indexLetter == _listDatas[index - 5].indexLetter);

    return _FriendCell(
      imageUrl: _listDatas[index - 4].imageUrl,
      name: _listDatas[index - 4].name,
      groupTitle: _hiddenIndexLetter ? null : _listDatas[index - 4].indexLetter,
    );
  }

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Scaffold(
        appBar: AppBar(
          // actions: [
          //   GestureDetector(
          //     onTap: () {
          //       // Navigator.of(context).push(MaterialPageRoute(
          //       //     builder: (BuildContext context) => DiscoverChildPage(
          //       //       title: '添加朋友',
          //       //     )));
          //     },
          //     child: Container(
          //       margin: EdgeInsets.only(right: 10),
          //       child: Image(
          //         image: AssetImage('images/icon_friends_add.png'),
          //         width: 25,
          //       ),
          //     ),
          //   ),
          // ],
          backgroundColor: ls.weChatThemColor,
          title: Text('通讯录'),
        ),
        body: Stack(
          children: [
            Container(
              color: ls.weChatThemColor,
              child: ListView.builder(
                controller: _scrollController,
                itemBuilder: _itemForRow,
                itemCount: _listDatas.length + _headerData.length,
              ),
            ), //列表
            ls.IndexBar(
              indexBarCallBack: (String str) {
                if (_groupOffsetMap[str] != null) {
                  _scrollController.animateTo(_groupOffsetMap[str],
                      duration: Duration(microseconds: 100),
                      curve: Curves.easeIn);
                }
              },
            ), //悬浮的索引条
          ],
        ));
  }
}

class Friends {
  Friends({this.imageUrl, this.name, this.indexLetter, this.imageAssets});
  final String? imageAssets;
  final String? imageUrl;
  final String? name;
  final String? indexLetter;
}

List<Friends> datas = [
  Friends(
      imageUrl: 'https://randomuser.me/api/portraits/women/27.jpg',
      name: 'Lina',
      indexLetter: 'L'),
  Friends(
      imageUrl: 'https://randomuser.me/api/portraits/women/17.jpg',
      name: '菲儿',
      indexLetter: 'F'),
  Friends(
      imageUrl: 'https://randomuser.me/api/portraits/women/16.jpg',
      name: '安莉',
      indexLetter: 'A'),
  Friends(
      imageUrl: 'https://randomuser.me/api/portraits/men/31.jpg',
      name: '阿贵',
      indexLetter: 'A'),
  Friends(
      imageUrl: 'https://randomuser.me/api/portraits/women/22.jpg',
      name: '贝拉',
      indexLetter: 'B'),
  Friends(
      imageUrl: 'https://randomuser.me/api/portraits/women/37.jpg',
      name: 'Lina',
      indexLetter: 'L'),
  Friends(
      imageUrl: 'https://randomuser.me/api/portraits/women/18.jpg',
      name: 'Nancy',
      indexLetter: 'N'),
  Friends(
      imageUrl: 'https://randomuser.me/api/portraits/men/47.jpg',
      name: '扣扣',
      indexLetter: 'K'),
  Friends(
      imageUrl: 'https://randomuser.me/api/portraits/men/3.jpg',
      name: 'Jack',
      indexLetter: 'J'),
  Friends(
      imageUrl: 'https://randomuser.me/api/portraits/women/5.jpg',
      name: 'Emma',
      indexLetter: 'E'),
  Friends(
      imageUrl: 'https://randomuser.me/api/portraits/women/24.jpg',
      name: 'Abby',
      indexLetter: 'A'),
  Friends(
      imageUrl: 'https://randomuser.me/api/portraits/men/15.jpg',
      name: 'Betty',
      indexLetter: 'B'),
  Friends(
      imageUrl: 'https://randomuser.me/api/portraits/men/13.jpg',
      name: 'Tony',
      indexLetter: 'T'),
  Friends(
      imageUrl: 'https://randomuser.me/api/portraits/men/26.jpg',
      name: 'Jerry',
      indexLetter: 'J'),
  Friends(
      imageUrl: 'https://randomuser.me/api/portraits/men/36.jpg',
      name: 'Colin',
      indexLetter: 'C'),
  Friends(
      imageUrl: 'https://randomuser.me/api/portraits/women/12.jpg',
      name: 'Haha',
      indexLetter: 'H'),
  Friends(
      imageUrl: 'https://randomuser.me/api/portraits/women/11.jpg',
      name: 'Ketty',
      indexLetter: 'K'),
  Friends(
      imageUrl: 'https://randomuser.me/api/portraits/women/13.jpg',
      name: 'Lina',
      indexLetter: 'L'),
  Friends(
      imageUrl: 'https://randomuser.me/api/portraits/women/23.jpg',
      name: 'Lina',
      indexLetter: 'L'),
];

然后到pubspec.yaml添加用到的图片。

在这里插入图片描述

然后回到package里面添加directory 并且将刚才新建的example工程的main.dart文件扔进去。

在这里插入图片描述
这样example就添加好了。

1.6 Part

如果一个包代码都在一个文件中,就会太拥挤,这个时候就可以用到part。这里在包里面的lib中创建一个index_bar文件,然后将在test_package_demo中写的内容都放到index_bar里面。然后添加下列代码告诉外界index_bar属于test_package_demo。

part of 'test_package_demo.dart';

然后在test_package_demo里面添加

part "index_bar.dart";

在这里插入图片描述
在这里插入图片描述

到这里package修改就完成了,将版本号修改一下然后重新上传

在这里插入图片描述
然后就可以看到Example和新的版本号了。

在这里插入图片描述

2. Plugin

如果要开发一个调用特定平台API的包,那么就需要开发一个插件包,插件包是Dart包的专用版本。

创建一个Plugin工程。可以看到这里默认有一个获取版本的方法。

在这里插入图片描述

在这里添加一个获取电量的方法。

  static Future<String?> get platformBatteryLevel async {
    int batteryLevel = await _channel.invokeMethod('platformBatteryLevel');
    return batteryLevel.toString();
  }

然后来到example中的iOS打开Runner.xcworkspace编写代码。将插件中iOS的FlutterPluginDemoPlugin的代码复制到Runner.xcworkspace的Appdelegate里面开始编写,因为这里面有提示编写比较方便。
在这里插入图片描述

在这里插入图片描述
然后后的代码:

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
  if ([@"getPlatformVersion" isEqualToString:call.method]) {
    result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
  } else if  ([@"platformBatteryLevel" isEqualToString:call.method])  {
      int batteryLevel = [self getBatteryLevel];
      result(@(batteryLevel));
  }else {
    result(FlutterMethodNotImplemented);
  }
}

- (int) getBatteryLevel {
    UIDevice *device = UIDevice.currentDevice;
    device.batteryMonitoringEnabled = YES;
    if (device.batteryLevel == UIDeviceBatteryStateUnknown) {
        return -1;
    } else {
        return (int)(device.batteryLevel * 100);
    }
}

然后将写好的代码放到FlutterPluginDemoPlugin.m里面。

在这里插入图片描述
接下来到example里面的main.dart进行试验。

在这里插入图片描述
运行后得到下图,这里因为是模拟器所以是-100.
在这里插入图片描述

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-12-04 13:33:24  更:2021-12-04 13:34:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年10日历 -2024/10/25 8:21:31-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码