Flutter 是 谷歌 開源的 UI 架構,旨在幫助開發者通過一套代碼庫高效建構多平台精美應用,支援移動、Web、桌面和嵌入式平台。相比較目前的混合開發方案,Flutter 提供了大量的文檔,能非常快速且友好的讓你加入到這個大家庭,針對移動端,Flutter 提供了符合 Android 風格的 Material 和符合 iOS 風格的 Cupertino,同時對不同平台也做了不同的相容。比如 閑魚,美團,騰訊 等大廠都有相關案例使用。感興趣的同學可以關注:
https://github.com/flutter/flutter
https://flutter.cn/
https://flutterchina.club/
https://pub.flutter-io.cn/
https://www.dartcn.com/
項目介紹
基于flutter+dart等技術開發仿微信手機端App聊天執行個體,實作消息發送/編輯器光标處插入表情,圖檔縮放預覽,長按PopupWin菜單,視訊/紅包/朋友圈等功能。
架構技術
- 編碼/技術:VS + Flutter 1.12.13/Dart 2.7.0
- 圖檔/拍照:image_picker: ^0.6.6+1
- 圖檔預覽元件:photo_view: ^0.9.2
- 視訊元件:chewie: ^0.9.7
- 存儲元件:shared_preferences: ^0.5.7+1
- 字型圖示:阿裡iconfont字型圖示庫
flutter項目結構/主入口main.dart配置
/** * @tpl Flutter入口頁面配置 */import 'package:flutter/material.dart';// 引入公共樣式import 'styles/common.dart';// 引入底部Tabbar頁面導航import 'components/tabbar.dart';// 引入位址路由import 'router/routes.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter App', debugShowCheckedModeBanner: false, theme: ThemeData( primaryColor: GStyle.appbarColor, ), home: TabBarPage(), onGenerateRoute: onGenerateRoute, ); }}
- 會話、通訊錄、我三大子產品,通過flutter導航切換BottomNavigationBar元件封裝到tabbar頁面
- 底部tab是放在 Scaffold 的 bottomNavigationBar 中,頂部tab是放在 AppBar 的 bottom 中,也就是标題欄之下
bottomNavigationBar: BottomNavigationBar( backgroundColor: GStyle.tabbarColor, fixedColor: GStyle.primaryColor, type: BottomNavigationBarType.fixed, elevation: 1.0, unselectedFontSize: 12.0, selectedFontSize: 12.0, currentIndex: _currentTabIndex, items: [ BottomNavigationBarItem( icon: Stack( alignment: Alignment(6,-4), children: [ GStyle.iconfont(0xe62a, size: 21.0), GStyle.badge(13), ], ), title: Text('會話') ), BottomNavigationBarItem( icon: GStyle.iconfont(0xe600, size: 21.0), title: Text('通訊錄') ), BottomNavigationBarItem( icon: Stack( alignment: Alignment(1.5, -1.5), children: [ GStyle.iconfont(0xe6b4, size: 21.0), GStyle.badge(0, isdot: true), ], ), title: Text('我') ), ], onTap: (int index) { setState(() { _currentTabIndex = index; }); },)
appBar: new AppBar( ... ///tabBar控件 bottom: new TabBar( ///頂部時,tabBar為可以滑動的模式 isScrollable: true, ///必須有的控制器,與pageView的控制器同步 controller: _tabController, ///每一個tab item,是一個List tabs: _tabItems, ///tab底部選中條顔色 indicatorColor: _indicatorColor, ), ...)
flutter中使用阿裡iconfont字型圖示
由于flutter自帶圖示不能滿足項目需求,是以項目中使用的圖示是阿裡iconfont字型圖示庫
在pubspec.yaml裡添加字型配置
Icon(IconData(0xe60e, fontFamily:'iconfont'), size:24.0)
IconData第一個參數是字型code,到這一步自定義字型圖示就好了,每次都這樣調用顯得不優雅,隻能作如下抽離封裝。
class GStyle { // __ 自定義圖示 static iconfont(int codePoint, {double size = 16.0, Color color}) { return Icon( IconData(codePoint, fontFamily: 'iconfont', matchTextDirection: true), size: size, color: color, ); } }
這樣調用的時候隻需傳入字型code就行,支援自定義圖示大小、顔色
GStyle.iconfont(0xe60e)GStyle.iconfont(0xe60e, color: Colors.orange, size: 21.0)
flutter未讀資訊圓形數字提示|紅點提醒
如下圖:類似這種未讀消息提醒,在app中很常見,給人很直覺的消息提示。在flutter中并沒有提供這個元件,隻能自行封裝。
class GStyle { // 消息紅點 static badge(int count, {Color color = Colors.red, bool isdot = false, double height = 18.0, double width = 18.0}) { final _num = count > 99 ? '···' : count; return Container( alignment: Alignment.center, height: !isdot ? height : height/2, width: !isdot ? width : width/2, decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(100.0)), child: !isdot ? Text('$_num', style: TextStyle(color: Colors.white, fontSize: 12.0)) : null ); }}
預設背景色是紅色、大小為18,數字超過99會以...顯示。
//紅點GStyle.badge(0, isdot:true)//預設數字13圓形GStyle.badge(13)//預設數字68, 橘色 大小15GStyle.badge(68, color: Colors.orange, height: 15.0, width: 15.0)
flutter自定義修改彈窗
flutter中提供了多種豐富的彈窗元件:對話框AlertDialog、底部彈出框showModalBottomSheet、底部弱提示框SnackBar
- flutter實作類似微信在長按位置彈出菜單
可通過flutter中InkWell元件提供的onTapDown擷取長按坐标點,onLongPress長按事件彈出菜單
/** * @tpl 首頁模闆 */import 'package:flutter/material.dart';import '../../styles/common.dart';import '../../components/headerbar.dart';class IndexPage extends StatefulWidget { @override _IndexPageState createState() => _IndexPageState();}class _IndexPageState extends State { double _globalPositionX = 0.0; //點選位置的橫坐标 double _globalPositionY = 0.0; //點選位置的縱坐标 @override Widget build(BuildContext context) { return Scaffold( backgroundColor: GStyle.backgroundColor, appBar: HeaderBar('Flutter聊天室'), body: Container( child: ListView( children: [ InkWell( splashColor: Colors.grey[200], child: Container( ... ), onTap: () {...}, //擷取長按坐标點 onTapDown: (TapDownDetails details) { _globalPositionX = details.globalPosition.dx; _globalPositionY = details.globalPosition.dy; }, onLongPress: () { _showPopupMenu(context); }, ), ], ), ), ); } // 彈出長按菜單 void _showPopupMenu(BuildContext context) { bool isLeft = _globalPositionX > MediaQuery.of(context).size.width/2 ? false : true; bool isTop = _globalPositionY > MediaQuery.of(context).size.height/2 ? false : true; showDialog( context: context, builder: (context) { return Stack( children: [ Positioned( top: isTop ? _globalPositionY : _globalPositionY - 200, left: isLeft ? _globalPositionX : _globalPositionX - 120, width: 120, child: Material( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2.0)), //圓角 child: Column( children: [ ...元件内容 ], ), ), ) ], ); } ); }}
- flutter對話框SimpleDialog,可以顯示附加的提示和操作,通常配合SimpleDialogOption一起使用
/** * @tpl 通訊錄頁面 */import 'package:flutter/material.dart';import '../../styles/common.dart';import '../../components/headerbar.dart';class ContactPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( backgroundColor: GStyle.backgroundColor, appBar: HeaderBar('通訊錄'), body: DefaultTabController( length: 2, child: Scaffold( appBar: TabBar( tabs: [ Tab(text: '好友清單'),Tab(text: '推薦好友'), ], unselectedLabelColor: Colors.black54, labelColor: GStyle.c_0091ea, indicatorColor: GStyle.c_0091ea, indicatorSize: TabBarIndicatorSize.label, ), body: TabBarView( children: [ //tab1頁面 Container( child: ListView( children: [ InkWell( splashColor: Colors.grey[200], child: Container( ... ), onTap: () { Navigator.pushNamed(context, '/uinfo'); }, onLongPress: () { _showSimplePopMenu(context); }, ), ], ), ), //tab2頁面 Container( ... ), ], ) ) ), ); } void _showSimplePopMenu(BuildContext context) { showDialog( context: context, builder: (context) { return SimpleDialog( // title: Text('标題'), contentPadding: EdgeInsets.symmetric(vertical: 5.0), elevation: 0, children: [ SimpleDialogOption( child: Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('添加備注')), onPressed: () {}, ), ... ], ); } ); }}
- flutter底部面闆showModalBottomSheet對話框,相當于彈出了一個新頁面
/** * @tpl 好友資訊頁面 */import 'package:flutter/material.dart';import '../../styles/common.dart';import '../../components/headerbarNav.dart';class UinfoPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( backgroundColor: GStyle.backgroundColor, appBar: HeaderBarNav('', leading: IconButton(icon: GStyle.iconfont(0xe65c, size: 17.0), onPressed: () {Navigator.pop(context);}), actions: [ IconButton(icon: GStyle.iconfont(0xe93e, size: 17.0), onPressed: () {_showBottomSheet(context);}), ] ), body: Container( ... ), ); } // 底部彈窗 void _showBottomSheet(BuildContext context) { showModalBottomSheet( context: context, builder: (context) { return Column( mainAxisSize: MainAxisSize.min, //主軸方向占有空間的值,預設是max children: [ ListTile( title: Row( mainAxisAlignment: MainAxisAlignment.center, children: [Text('推薦給朋友', style: TextStyle(fontSize: 14))] ), onTap: () {}, ), ... ], ); }, // 圓角 shape: RoundedRectangleBorder( borderRadius: BorderRadius.only(topLeft: Radius.circular(10.0), topRight: Radius.circular(10.0)) ) ); }}
- flutter中AlertDialog對話框
在flutter中AlertDialog彈窗預設大小是固定的,如何去掉寬高限制?
通過SizedBox和無限制容器UnconstrainedBox元件配合實作,如下圖寬度設定為260,高度是自适應
void _showCardPopup(BuildContext context) { showDialog( context: context, builder: (context) { return UnconstrainedBox( constrainedAxis: Axis.vertical, child: SizedBox( width: 260, child: AlertDialog( content: Container( ... ), elevation: 0, contentPadding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0), ), ), ); } );}
行,到這裡基于flutter/dart開發聊天執行個體就介紹完了,希望這些分享能有些許幫助!!
最後附上兩個最近執行個體項目
electron-vue仿微信用戶端|electron聊天執行個體
uniapp+nvue仿抖音效果|小視訊直播室