Flutter了解之入門篇4-1(常用三方庫)

  package_info 獲取應(yīng)用相關(guān)信息(版本號、應(yīng)用名、包名、構(gòu)建版本號等)
  url_launcher 跳轉(zhuǎn)到其他應(yīng)用/瀏覽器
  flutter_easyrefresh 上下拉刷新
  refresh_indicator  上下拉刷新
  logging 日志打印
  shared_preferences 持久化鍵值對
  sqflite 持久化數(shù)據(jù)庫
  marquee 滾動文字組件
  carousel_slider 
  cached_network_image 網(wǎng)絡(luò)圖片 加載緩存
  flutter_cache_manager
  video_player
  path_provider 文件管理
  cookie_jar
  catcher 全局異常捕獲
  flutter_swiper 輪播圖
  FocusedMenuHolder 長按
  flutter_easyloading 防重提交時的loading蒙版
  socket_io_client(WebSocket插件)
  fl_chart 圖表插件(線條圖、柱狀圖、散點(diǎn)圖、餅圖)
  functional_widget 自動生成類組件
  motion_toast 彈框

package_info 獲取應(yīng)用相關(guān)信息(版本號、應(yīng)用名、包名、構(gòu)建版本號等)

  import 'package:package_info/package_info.dart';
  PackageInfo.fromPlatform().then((packageInfo){
  });

注意:
  1. 如果沒有給設(shè)置iOS的displayName會導(dǎo)致獲取不到appName,運(yùn)行會提示出錯。

url_launcher 跳轉(zhuǎn)到其他應(yīng)用/瀏覽器

  import 'package:url_launcher/url_launcher.dart';
  const _url = 'https://flutter.cn';
  void _launchURL() async => await canLaunch(_url) ? await launch(_url) : throw '不能打開 $_url';

flutter_easyrefresh 上下拉刷新

  import 'package:flutter_easyrefresh/easy_refresh.dart';
  EasyRefresh(
    child: ScrollView(),
    onRefresh: () async{
    },
    onLoad: () async {
    },
  )

首次加載時并不會自動加載,需要使用 EasyRefreshController 來控制。
支持自定義header和footer

refresh_indicator 上下拉刷新


logging 日志打印


shared_preferences 持久化鍵值對

類似于iOS的NSUserDefaults、Android的SharedPreferences
可用來存儲:布爾值、整型、浮點(diǎn)型、字符串、字符串?dāng)?shù)組。對象需要做json序列化后才能存儲。

import 'package:shared_preferences/shared_preferences.dart';
_incrementCounter() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  int counter = (prefs.getInt('counter') ?? 0) + 1;
  await prefs.setInt('counter', counter);
}

sqflite 持久化數(shù)據(jù)庫

// Get a location using getDatabasesPath
var databasesPath = await getDatabasesPath();
String path = join(databasesPath, 'demo.db');

// Delete the database
await deleteDatabase(path);

// open the database
Database database = await openDatabase(path, version: 1,
    onCreate: (Database db, int version) async {
  // When creating the db, create the table
  await db.execute(
      'CREATE TABLE Test (id INTEGER PRIMARY KEY, name TEXT, value INTEGER, num REAL)');
});

// Insert some records in a transaction
await database.transaction((txn) async {
  int id1 = await txn.rawInsert(
      'INSERT INTO Test(name, value, num) VALUES("some name", 1234, 456.789)');
  print('inserted1: $id1');
  int id2 = await txn.rawInsert(
      'INSERT INTO Test(name, value, num) VALUES(?, ?, ?)',
      ['another name', 12345678, 3.1416]);
  print('inserted2: $id2');
});

// Update some record
int count = await database.rawUpdate(
    'UPDATE Test SET name = ?, value = ? WHERE name = ?',
    ['updated name', '9876', 'some name']);
print('updated: $count');

// Get the records
List<Map> list = await database.rawQuery('SELECT * FROM Test');
List<Map> expectedList = [
  {'name': 'updated name', 'id': 1, 'value': 9876, 'num': 456.789},
  {'name': 'another name', 'id': 2, 'value': 12345678, 'num': 3.1416}
];
print(list);
print(expectedList);
assert(const DeepCollectionEquality().equals(list, expectedList));

// Count the records
count = Sqflite
    .firstIntValue(await database.rawQuery('SELECT COUNT(*) FROM Test'));
assert(count == 2);

// Delete a record
count = await database
    .rawDelete('DELETE FROM Test WHERE name = ?', ['another name']);
assert(count == 1);

// Close the database
await database.close();

marquee 滾動文字組件

支持設(shè)置文字的多種參數(shù)(如字體,滾動方向,留白,滾動速度)

Marquee(
  text: 'Some sample text that takes some space.',
  style: TextStyle(fontWeight: FontWeight.bold),
  scrollAxis: Axis.horizontal,
  crossAxisAlignment: CrossAxisAlignment.start,
  blankSpace: 20.0,
  velocity: 100.0,
  pauseAfterRound: Duration(seconds: 1),
  startPadding: 10.0,
  accelerationDuration: Duration(seconds: 1),
  accelerationCurve: Curves.linear,
  decelerationDuration: Duration(milliseconds: 500),
  decelerationCurve: Curves.easeOut,
)

carousel_slider 輪播圖

cached_network_image 網(wǎng)絡(luò)圖片 加載緩存




flutter_cache_manager

video_player

path_provider 文件管理

getApplicationDocumentsDirectory
  應(yīng)用文檔目錄(不會被系統(tǒng)清除,用戶數(shù)據(jù)存儲目錄),對于安卓推薦使用外部存儲getExternalStorageDirectory。
getLibraryDirectory
  指向應(yīng)用可以持久存儲數(shù)據(jù)的目錄,不支持安卓平臺。
getTemporaryDirectory
  應(yīng)用臨時目錄(可能被清除)
getApplicationSupportDirectory
  應(yīng)用支持目錄,一般放置與用戶無關(guān)的數(shù)據(jù)。
getExternalStorageDirectory
  獲取外部存儲目錄,不支持 iOS 平臺。
getExternalCacheDirectories
  獲取外部緩存目錄,,不支持 iOS 平臺。
getExternalStorageDirectories
  獲取外部的目錄列表,不支持 iOS 平臺。
getDownloadsDirectory
  獲取下載目錄,用于 Web 端,不支持安卓和 iOS平臺。

例
getTemporaryDirectory().then((tempDir) => {_destPath = tempDir.path + 'googlechrome.dmg'});

1. OS Error: Read-only file system
  安卓系統(tǒng)需要獲取READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE權(quán)限。
2. onReceivedProgress中total=-1
  表示該文件被壓縮或者需要會話信息才可以下載(如后端開啟了驗(yàn)證)。
3. 刪除文件的時候需要檢查文件是否在下載過程中,如果在下載過程中刪除會引起文件讀寫沖突,拋出異常。
4. CancelToken一個實(shí)例只能取消一次請求
  因此每次發(fā)起請求的時候需要重新構(gòu)建CancelToken對象,否則取消一次后無法再次取消。

cookie_jar

catcher 全局異常捕獲

import 'package:catcher/catcher.dart';
main() {
  // 1. 
  // 創(chuàng)建debugCatcher,錯誤發(fā)生后會彈框,并輸出到控制臺
  CatcherOptions debugOptions =
      CatcherOptions(DialogReportMode(), [ConsoleHandler()]);
  // 創(chuàng)建releaseCatcher,錯誤發(fā)生后會彈框,并將錯誤發(fā)送到郵箱
  CatcherOptions releaseOptions = CatcherOptions(DialogReportMode(), [
    EmailManualHandler(["support@email.com"])
  ]);
  // 2. 
  Catcher(rootWidget: MyApp(), debugConfig: debugOptions, releaseConfig: releaseOptions);
}

自動捕捉到系統(tǒng)的異常,并且可以指定異常上報(bào)地址自動將運(yùn)行錯誤上報(bào)給服務(wù)端。

flutter_swiper 輪播圖

1. 添加依賴包,并下載(pubspec.yaml文件中dependencies下)
flutter_swiper: #lastversion

2. 導(dǎo)入包
import 'package:flutter_swiper/flutter_swiper.dart';

3. 使用
var itemList=[
  {
    "url":"http://..."
  },
  {
    "url":""http://..."
  },
  {
    "url":"http://..."
  },
];
// 通常會在外層使用Container、AspectRatio
Container(
  child: AspectRatio(
    aspectRatio: 16 / 9,  // 寬高比例
    child: Swiper(
      itemBuilder: (BuildContext context, int index) {
        // item項(xiàng)
        return Image.network(
          this.itemList[index]["url"],
          fit: BoxFit.fill,
        );
      },
      itemCount: 3, // item個數(shù)
      pagination: new SwiperPagination(), // 分頁器
      control: new SwiperControl(), // 左右箭頭
      // viewportFraction: 0.8,
      // scale: 0.9,
    ),
  ),
),

FocusedMenuHolder 長按

@override
Widget build(BuildContext context) {
  return FocusedMenuHolder(
    child: Container(
      // 省略原列表元素構(gòu)建代碼
    ),
    onPressed: () {
      // 點(diǎn)擊事件
      _handlePressed(context);
    },
    // 長按菜單
    menuItems: <FocusedMenuItem>[
      FocusedMenuItem(
          title: Text("查看詳情"),
          trailingIcon: Icon(Icons.remove_red_eye_outlined),
          onPressed: () {
            _handlePressed(context);
          }),
      FocusedMenuItem(
        title: Text("取消"),
        trailingIcon: Icon(Icons.cancel),
        onPressed: () {},
      ),
      FocusedMenuItem(
          title: Text(
            "刪除",
            style: TextStyle(color: Colors.redAccent),
          ),
          trailingIcon: Icon(
            Icons.delete,
            color: Colors.redAccent,
          ),
          onPressed: () {
            handleDelete(dynamicEntity.id);
          }),
    ],
  );
}

flutter_easyloading 防重提交時的loading蒙版

_handleSubmit() async {
  //...校驗(yàn)代碼
    EasyLoading.showInfo('請稍候...', maskType: EasyLoadingMaskType.black);
  //...網(wǎng)絡(luò)請求代碼
  EasyLoading.dismiss();
}

socket_io_client(WebSocket插件,對Dart的Socket進(jìn)行了封裝)

創(chuàng)建連接
// autoConnect:創(chuàng)建后是否自動連接(默認(rèn):自動連接)。forceNew:是否每次都新建Socket對象,false(默認(rèn))則使用舊鏈接
_socket = SocketIO.io('ws://$host:$port', <String, dynamic>{
  'transports': ['websocket'],
  'autoConnect': true,
  'forceNew': true
});

socket常用的響應(yīng)回調(diào)
  1. onConnect(EventHandler handler)
    連接建立成功回調(diào);
  2. onConnectTimeout(EventHandler handler)
    連接超時回調(diào);
  3. onConnectError(EventHandler handler)
    連接錯誤回調(diào);
  4. onError(EventHandler handler)
    發(fā)生錯誤時回調(diào);
  5. on(String event, (EventHandler handler)
    訂閱指定事件的消息,服務(wù)端發(fā)送該事件的消息時可以在該函數(shù)接收。
  6. onDisconnect(EventHandler handler)
    斷開連接時回調(diào);
  7. connect/disconnect
    主動連接/斷開連接方法;
  8. open/close
    打開和關(guān)閉方法。

示例(與服務(wù)端通信)

class StreamSocket {
  // StreamController 只允許有一個訂閱者,可以使用 sink 屬性的 add 方法添加新的流數(shù)據(jù),完成添加后會通知其訂閱者(StreamProvider)有新的數(shù)據(jù)產(chǎn)生。
  final _socketResponse = StreamController<String>();
  Stream<String> get getResponse => _socketResponse.stream;
  final String host;
  final int port;
  late final Socket _socket;
  StreamSocket({required this.host, required this.port}) {
    _socket = SocketIO.io('ws://$host:$port', <String, dynamic>{
      'transports': ['websocket'],
      'autoConnect': true,
      'forceNew': true
    });
  }
  void connectAndListen() {
    _socket.onConnect((_) {
      debugPrint('connected');
    });
    _socket.onConnectTimeout((data) => debugPrint('timeout'));
    _socket.onConnectError((error) => debugPrint(error.toString()));
    _socket.onError((error) => debugPrint(error.toString()));
    _socket.on('msg', (data) {
      _socketResponse.sink.add(data);
    });
    _socket.onDisconnect((_) => debugPrint('disconnect'));
  }
  void sendTextMessage(String message) {
    _socket.emit('msg', message);
  }
  void close() {
    _socketResponse.close();
    _socket.disconnect().close();
  }
}
class _SocketClientWrapperState extends State<SocketClientWrapper> {
  final StreamSocket streamSocket = StreamSocket(host: '127.0.0.1', port: 3001);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Stream Provicer'),
      ),
      body: Stack(
        alignment: Alignment.bottomCenter,
        children: [
          StreamProvider<String>(
            create: (context) => streamSocket.getResponse,
            initialData: '',
            child: StreamDemo(),
          ),
          ChangeNotifierProvider<MessageModel>(
            child: MessageReplyBar(messageSendHandler: (message) {
              streamSocket.sendTextMessage(message);
            }),
            create: (context) => MessageModel(),
          ),
        ],
      ),
    );
  }
  @override
  void initState() {
    streamSocket.connectAndListen();  
    super.initState();
  }
  @override
  void dispose() {
    streamSocket.close();  
    super.dispose();
  }
}

示例(與其他客戶端通信)

即時聊天
    1. 在建立連接后,客戶端發(fā)送消息將用戶唯一標(biāo)識符與連接的socket對象進(jìn)行綁定。
    2. 當(dāng)其他用戶發(fā)送消息給該用戶時,找到該用戶綁定的socket對象,再通過該socket的emit方法發(fā)送消息。
/*
Socket可以發(fā)送字符串或Json 對象。

約定消息聊天為Json對象,字段如下:
  fromUserId:消息來源用戶 id;
  toUserId:接收消息用戶 id;
  contentType:消息類型,文本、圖片、語音、視頻等。
  content:消息內(nèi)容。
*/
class StreamSocket<T> {  
  final _socketResponse = StreamController<T>();
  Stream<T> get getResponse => _socketResponse.stream;
  final String host;
  final int port;
  late final Socket _socket;
  final String recvEvent;
  StreamSocket(
      {required this.host, required this.port, required this.recvEvent}) {
    _socket = SocketIO.io('ws://$host:$port', <String, dynamic>{
      'transports': ['websocket'],
      'autoConnect': true,
      'forceNew': true
    });
  }
  void connectAndListen() {
    _socket.onConnect((_) {
      debugPrint('connected');
    });
    _socket.onConnectTimeout((data) => debugPrint('timeout'));
    _socket.onConnectError((error) => debugPrint(error.toString()));
    _socket.onError((error) => debugPrint(error.toString()));
    _socket.on(recvEvent, (data) {
      _socketResponse.sink.add(data);
    });
    _socket.onDisconnect((_) => debugPrint('disconnect'));
  }
  void regsiter(String userId) {
    _socket.emit('register', userId);
  }
  void unregsiter(String userId) {
    _socket.emit('unregister', userId);
  }
  void sendMessage(String event, T message) {
    _socket.emit(event, message);  // 會調(diào)用對象的 toJson 將對象轉(zhuǎn)為 Json 對象發(fā)送,因此泛型的類需要實(shí)現(xiàn) Map<String dynamic> toJson 方法。
  }
  void close() {
    _socketResponse.close();
    _socket.disconnect().close();
  }
}
class _ChatWithUserPageState extends State<ChatWithUserPage> {
  late final StreamSocket<Map<String, dynamic>> streamSocket;
  @override
  void initState() {
    super.initState();
    streamSocket =
        StreamSocket(host: '127.0.0.1', port: 3001, recvEvent: 'chat');
    streamSocket.connectAndListen();
    streamSocket.regsiter('user1');
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('即時聊天'),
      ),
      body: Stack(
        alignment: Alignment.bottomCenter,
        children: [
          StreamProvider<Map<String, dynamic>?>(
            create: (context) => streamSocket.getResponse,
            initialData: null,
            child: StreamDemo(),
          ),
          ChangeNotifierProvider<MessageModel>(
            child: MessageReplyBar(messageSendHandler: (message) {
              Map<String, String> json = {
                'fromUserId': 'user1',
                'toUserId': 'user2',
                'contentType': 'text',
                'content': message
              };
              streamSocket.sendMessage('chat', json);
            }),
            create: (context) => MessageModel(),
          ),
        ],
      ),
    );
  }
  @override
  void dispose() {
    streamSocket.unregsiter('user1');
    streamSocket.close();
    super.dispose();
  }
}

fl_chart 圖表插件(線條圖、柱狀圖、散點(diǎn)圖、餅圖)

LineChartData 曲線圖
  1. lineTouchData
    數(shù)據(jù)觸點(diǎn)配置數(shù)據(jù),即觸摸到數(shù)據(jù)點(diǎn)后如何顯示
  2. gridData
    網(wǎng)格數(shù)據(jù)
  3. titlesData
    上下左右四個方位的標(biāo)題欄(坐標(biāo)軸欄)數(shù)據(jù),可以根據(jù)自己需要配置坐標(biāo)軸顯示以及標(biāo)題;
  4. borderData
    邊框數(shù)據(jù),及是否要顯示邊框,以及如何顯示邊框;
  5. lineBarsData
    數(shù)據(jù)點(diǎn)數(shù)組,可以在同一個圖表中顯示多條曲線。
  6. minX,maxX,minY和 MaxY
    X 軸和 Y 軸的最大最小值范圍,保證曲線顯示在屏幕范圍內(nèi)。

示例(使用到MobX)

class LineChartDemo extends StatelessWidget {
  LineChartDemo({Key? key}) : super(key: key);
  final ChartStore store = ChartStore();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('曲線')),
      body: Observer(
        builder: (context) => LineChart(
          sampleData1(store.lineYData),
          swapAnimationDuration: const Duration(milliseconds: 250),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          store.featchLineData();
        },
        child: Icon(Icons.refresh, color: Colors.white),
      ),
    );
  }
  // 曲線圖
  LineChartData sampleData1(List<double> yData) => LineChartData(
    lineTouchData: lineTouchData1,
    gridData: gridData,
    titlesData: titlesData1,
    borderData: borderData,
    lineBarsData: lineBarsData1(yData),
    minX: 0,
    maxX: 15,
    maxY: yData.reduce((value, element) => value < element ? element : value).toDouble()+1.0,
    minY: yData.reduce((value, element) => value > element ? element : value).toDouble()-1.0,
  );
  // 曲線數(shù)據(jù)點(diǎn)
  List<LineChartBarData> lineBarsData1(List<double> yData) {
    double x = 1;
    return [
      LineChartBarData(
        // 曲線還是折線
        isCurved: true,
        // 線條顏色
        colors: [const Color(0xff4a99fa)],
        // 線粗細(xì)
        barWidth: 2,
        // 是否圓形筆頭
        isStrokeCapRound: true,
        // 是否顯示點(diǎn)數(shù)據(jù)
        dotData: FlDotData(show: true),
        // 圖形覆蓋區(qū)域是否顯示
        belowBarData: BarAreaData(show: true),
        // 坐標(biāo)(x,y) 點(diǎn)集合
        spots: yData.map((value) {
          FlSpot spot = FlSpot(x, value.toDouble());
          x += 2;
          return spot;
        }).toList(),
      ),
    ];
  }
}
//
import 'package:mobx/mobx.dart';
import 'chart_service.dart';
part 'chart_store.g.dart';
class ChartStore = ChartStoreBase with _$ChartStore;
abstract class ChartStoreBase with Store {
  @observable
  List<double> lineYData = [0, 0, 0, 0, 0, 0, 0];
  @action
  Future<void> featchLineData() async {
    var response = await ChartService.getLines();
    if (response?.statusCode == 200) {
      lineYData =
          List<double>.from(response.data.map((e) => e.toDouble()).toList());
    }
  }
}

functional_widget 自動生成類組件

motion_toast 彈框

特性
  可以通過動畫圖標(biāo)實(shí)現(xiàn)動效;
  內(nèi)置了成功、警告、錯誤、提醒和刪除類型;
  支持自定義;
  支持不同的主題色;
  支持 null safety;
  心跳動畫效果;
  完全自定義的文本內(nèi)容;
  內(nèi)置動畫效果;
  支持自定義布局(LTR 和 RTL);
  自定義持續(xù)時長;
  自定義展現(xiàn)位置(居中,底部或頂部);
  支持長文本顯示;
  自定義背景樣式;
  自定義消失形式。

使用
  // 成功提示
  MotionToast.success(description: 'hello world').show(context);

內(nèi)置Toast

支持修改默認(rèn)參數(shù)(標(biāo)題、位置、寬度、顯示位置、動畫曲線等)

// 錯誤提示
MotionToast.error(
  description: '發(fā)生錯誤!',
  width: 300,
  position: MOTION_TOAST_POSITION.center,
).show(context);

// 刪除提示
MotionToast.delete(
  description: '已成功刪除',
  position: MOTION_TOAST_POSITION.bottom,
  animationType: ANIMATION.fromLeft,
  animationCurve: Curves.bounceIn,
).show(context);

// 信息提醒(帶標(biāo)題)
 MotionToast.info(
  description: '這是一條提醒,可能會有很多行。toast 會自動調(diào)整高度顯示',
  title: '提醒',
  titleStyle: TextStyle(fontWeight: FontWeight.bold),
  position: MOTION_TOAST_POSITION.bottom,  // position和animationType存在一定互斥關(guān)系
  animationType: ANIMATION.fromBottom,
  animationCurve: Curves.linear,
  dismissable: true,  // 只對顯示位置在底部時有用,為true時點(diǎn)擊空白處提前消失toast。
).show(context);

自定義Toast

使用MotionToast構(gòu)建一個實(shí)例。
MotionToast(
  title:'',  // 標(biāo)題
  description: '這是自定義 toast',  // 必傳
  icon: Icons.flag,  // 必傳。圖標(biāo),IconData 類,可以使用系統(tǒng)字體圖標(biāo)。
  primaryColor: Colors.blue,  // 必傳。主顏色(即大背景色)
  secondaryColor: Colors.green[300],  // 輔助色(圖標(biāo)和旁邊的豎條的顏色)
  titleStyle: TextStyle(  // 標(biāo)題的字體樣式
    color: Colors.white,
  ),
  descriptionStyle: TextStyle(  // toast文字的字體樣式
    color: Colors.white,
  ),
  toastDuration: 3000,  // 持續(xù)時間
  backgroundType: ,  // 背景類型,枚舉:transparent,solid和 lighter(默認(rèn))。
  position: MOTION_TOAST_POSITION.center,  // 顯示位置
  animationType: ANIMATION.fromRight,  // 動畫方向
  animationCurve: Curves.easeIn,  // 動畫曲線
  onClose:,  // 關(guān)閉時會調(diào)用
).show(context);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容