您好,登錄后才能下訂單哦!
本篇內容介紹了“Flutter開發中的技巧有哪些”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
1.部件溢出
異常大致如下:
A RenderFlex overflowed by 22 pixels on the bottom.
導致的原因就是在水平或者垂直方向上的內容超過了父部件的大小。一般來說我們的頁面不存在這樣的問題,因為根據頁面的設計,事先可以預料到是否超出。不過要注意到有輸入法彈出的頁面。比如我下面的這個例子:
可以看到底部溢出了22個像素,可能在18:9的手機以上不太會出現這種問題,因為屏幕的高度足夠。但是這種16:9的手機可能會暴露出來。解決的方法有兩種:
包一層SingleChildScrollView,讓你的頁面可以滑動起來。
在Scaffold中設置resizeToAvoidBottomInset為false。默認為ture,防止部件被遮擋。如果使用了這個方法,如果底部有輸入框,則會造成遮擋。
大家可以根據實際需求選擇。
2.輸入框的遮擋
頁面如下:
底部有輸入框,同時“提交”的按鈕固定在底部。一開始覺得既然固定在底部,那就使用Stack配合Positioned來實現,然而就導致輸入法彈出時,發生遮擋。
上圖中,我選中了***一個輸入框,但因為輸入法默認都是在輸入框的下方彈出,然而上面蓋著這個“提交”按鈕,發生了遮擋。
最終我的解決方法就是使用Column配合Expanded來實現。修復后如下:
3.SafeArea
一旦有部件固定在頂部或者底部(嚴謹點的話可以說是在屏幕的四邊)。那我我們***使用SafeArea來包一下。因為Android 和 IOS都有狀態欄,甚至IOS還有叫做“HomeIndicator”的橫條。所以一不留神就會出現適配問題。
我們在Flutter中常使用的BottomNavigationBar 和 AppBar 其實就在內部處理了此類問題。以 AppBar源碼為例:
class _AppBarState extends State { @override Widget build(BuildContext context) { if (widget.primary) { appBar = SafeArea( // <--- 1 top: true, child: appBar, ); } return Semantics( container: true, child: AnnotatedRegion( value: overlayStyle, child: Material( // <--- 2 color: widget.backgroundColor ?? appBarTheme.color ?? themeData.primaryColor, child: Semantics( explicitChildNodes: true, child: appBar, ), ), ), ); } }
所以使用方法為:
Material( // 需要顏色填充到邊界區域可以使用
Material( // 需要顏色填充到邊界區域可以使用 color: Colors.white, child: SafeArea( child: Container(), ), )
還是上面的頁面,我們對比一下處理前后的效果:
4.善用Theme
Flutter 在開發中,讓人詬病的就是大量的嵌套,而我們只能盡量避免。比如將一些部件、屬性進行封裝,避免重復的書寫。不過封裝也講究使用場景。如果這種樣式的部件僅僅只是某一兩處使用,封裝顯得有點小題大做。并且封裝的大而全也會增加使用的復雜度。那么這時就可以使用Theme這種辦法。
舉一個例子,在下圖中圈起來的部分有三個按鈕,它們的高度相同,文字、圓角大小也相同。如果每一個都去設定這些屬性,未免太過麻煩。
這時我們使用Theme去統一修改它們的樣式,就會很方便了。
Theme( data: Theme.of(context).copyWith( buttonTheme: ButtonThemeData( padding: const EdgeInsets.symmetric(horizontal: 16.0), minWidth: 64.0, height: 30.0, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, shape:RoundedRectangleBorder( borderRadius: BorderRadius.circular(4.0), ) ), textTheme: TextTheme( button: TextStyle( fontSize: 14.0, ) ) ), child: Row( children: [ FlatButton( color: Color(0xFFF6F6F6), onPressed: (){}, child: Text("聯系客戶"), ), ...... FlatButton( color: Color(0xFFF6F6F6), onPressed: (){}, child: Text("拒單"), ) ], ), )
同時使用Theme還可以修改許多默認的設置,比如FlatButton的默認寬度為88,高度為36,但是FlatButton中沒有直接修改的屬性,網上好多的方法都是通過包一層Container去修改,不僅增加的嵌套,有些需求還不能達到。所以善用Theme可以讓你省時省力,不過缺點就是你需要去翻翻源碼,尋找使用這些Theme的地方。
5.注意平臺差異
注意部分組件在Android與IOS平臺之間的差異。
(1)Scaffold的 AppBar,AppBar中默認的title在Android中靠左顯示,IOS中居中顯示。如果需要兩個平臺效果統一,需要設置在AppBar中主動設置centerTitle屬性。同時AppBar的返回箭頭圖標也不相同,統一的話需要自定義leading。
(2)頁面跳轉如果使用MaterialPageRoute來做過渡效果,注意Android中新的頁面會從屏幕底部滑動到屏幕頂部,IOS中新的頁面會從屏幕右側滑動到屏幕左側。
如果需要兩個平臺效果統一,我們不使用自帶效果,可以自定義一個。
Navigator.push(context, PageRouteBuilder(transitionDuration: Duration(milliseconds: 300), pageBuilder: (context, animation, secondaryAnimation){ return new FadeTransition( //使用漸隱漸入過渡, opacity: animation, child: TestPage(), ); }) );
要么修改Theme,統一兩平臺的實現。:
class MyApp extends StatelessWidget { static const Map _defaultBuilders = { TargetPlatform.android: FadeUpwardsPageTransitionsBuilder(), TargetPlatform.iOS: FadeUpwardsPageTransitionsBuilder(), }; @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( pageTransitionsTheme: PageTransitionsTheme( builders: _defaultBuilders ) ), ... ); } }
(3)ScrollPhysics效果,可以滑動的部件都有一個physics屬性。滑動到邊界時,Android平臺為邊緣陰影的效果ClampingScrollPhysics,IOS為回彈效果BouncingScrollPhysics。如果需要統一,可以指定physics屬性。
(4)狀態欄方面,Android平臺默認是半透明的效果,IOS則是透明效果。比如Android要實現IOS的效果,可以設置狀態欄為透明。不過IOS要實現Android的效果則不行。。。,難道只能自定義?有知道方法的可以分享一下。
void main(){ runApp(MyApp()); // 透明狀態欄 if (Platform.isAndroid) { SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle(statusBarColor: Colors.transparent); SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle); } }
5.輸入鍵盤
當TextField的keyboardType屬性設置為TextInputType.phone 或TextInputType.number時,IOS系統彈出的數字輸入鍵盤沒有"完成"按鈕,導致輸入法無法關閉。當然了Android不存在這個問題。
比較成熟有效的方案是在鍵盤彈出的上方懸浮一個按鈕,點擊可以關閉鍵盤。當然了,這種問題也有對應的庫可以解決,我使用的是flutter_keyboard_actions來解決了這個問題。因為在Android端我發現了部分輸入法的兼容問題,所以只針對IOS做了處理。大家可以看一下前后對比圖,具體實現代碼可以參考flutter_keyboard_actions的文檔和我的項目代碼:
當然平臺差異不僅僅是這么多,比如IOS自帶側滑返回等。具體我們可以去查看調用TargetPlatform枚舉類的代碼。
如果你覺得這樣真麻煩,我給你支個大招,修改ThemeData的platform,指定一個平臺。
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( platform: TargetPlatform.android ), ... ); } }
其次就是使用TextInputType.number在IOS中彈起的鍵盤沒有小數點符號。在輸入金額類型數據時,需要將keyboardType屬性設置為TextInputType.numberWithOptions(decimal: true)。
6.keyboardType
keyboardType屬性主要含義為彈起的鍵盤類型,并不代表輸入數據的類型。
而在Android開發中,在EditText中設置android:inputType不僅可以指定彈起的鍵盤類型,同時也確定了輸入數據的類型,也就是內置了數據的格式校驗。Flutter中并沒有后者,所以可能一開始你是TextInputType.number,但是在輸入法中切換成中文鍵盤,一樣可以輸入中文字符。所以數據的校驗需要我們使用inputFormatters自己處理。
比如TextInputType.phone時可以使用WhitelistingTextInputFormatter 白名單校驗,只允許輸入0~9:
TextField( keyboardType: TextInputType.phone, inputFormatters: [WhitelistingTextInputFormatter(RegExp("[0-9]"))] )
輸入密碼時可以使用BlacklistingTextInputFormatter 黑名單校驗,除去中文字符:
TextField( keyboardType: TextInputType.text, inputFormatters: [BlacklistingTextInputFormatter(RegExp("[\u4e00-\u9fa5]"))] )
輸入小數時,可以自定義TextInputFormatter來限制輸入小數格式:
TextField( keyboardType: TextInputType.numberWithOptions(decimal: true), inputFormatters: [UsNumberTextInputFormatter()] ) //來源:https://www.cnblogs.com/yangyxd/p/9639588.html class UsNumberTextInputFormatter extends TextInputFormatter { static const defaultDouble = 0.001; static double strToFloat(String str, [double defaultValue = defaultDouble]) { try { return double.parse(str); } catch (e) { return defaultValue; } } @override TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { String value = newValue.text; int selectionIndex = newValue.selection.end; if (value == ".") { value = "0."; selectionIndex++; } else if (value != "" && value != defaultDouble.toString() && strToFloat(value, defaultDouble) == defaultDouble) { value = oldValue.text; selectionIndex = oldValue.selection.end; } return new TextEditingValue( text: value, selection: new TextSelection.collapsed(offset: selectionIndex), ); } }
7.InkWell
InkWell有的叫濺墨效果,有的叫水波紋效果。使用場景是給一些無點擊事件的部件添加點擊事件時使用(也支持長按、雙擊等事件),同時你也可以去修改它的顏色和形狀。
InkWell( borderRadius: BorderRadius.circular(8.0), // 圓角 splashColor: Colors.transparent, // 濺墨色(波紋色) highlightColor: Colors.transparent, // 點擊時的背景色(高亮色) onTap: () {},// 點擊事件 child: Container(), );
不過有時你會發現并不是包一層InkWell就一定會有濺墨效果。主要原因是濺墨效果是在一個背景效果,并不是覆蓋的前景效果。所以InkWell中的child一旦有設置背景圖或背景色,那么就會遮住這個濺墨效果。如果你需要這個濺墨效果,有兩種方式實現。
(1)包一層 Material,將背景色設置在 Material中的color里。
Material( color: Colors.white, child: InkWell(), )
(2)使用Stack布局,將InkWell放置在上層。這種適用于給圖片添加點擊效果,比如Banner圖的點擊。
Stack( children: [ Positioned.fill( child: Image(), ), Positioned.fill( child: Material( color: Colors.transparent, child: InkWell( splashColor: Color(0X40FFFFFF), highlightColor: Colors.transparent, onTap: () {}, ), ), ) ], )
8.保持頁面狀態
比如點擊導航欄來回切換頁面,默認情況下會丟失原頁面狀態,也就是每次切換都會重新初始化頁面。這種情況解決方法就是PageView與BottomNavigationBar結合使用,同時子頁面State中繼承AutomaticKeepAliveClientMixin并重寫wantKeepAlive為true。代碼大致如下:
class _TestState extends State with AutomaticKeepAliveClientMixin{ @override Widget build(BuildContext context) { super.build(context); return Container(); } @override bool get wantKeepAlive => true; }
9.依賴版本問題
首先這里建議凡是Flutter的插件在填寫版本號時不要使用^符號。
^符號意味著你可以使用此插件的***版本(大于等于當前版本)。這會導致什么問題呢?可能你前一天代碼還能跑起來,今天就編譯出錯了。因為這些插件中包括Android、IOS的所用依賴環境配置,常見的就是新版本使用了AndroidX的依賴,但是還有些插件并沒有使用AndroidX,導致了兩者的沖突。
我之前在看flutter-go的代碼時,就是因為webview的插件突然升級了,導致了安裝失敗。具體問題可以看這里。所以在代碼穩定的情況下不建議使用^符號。
發生了這種問題,有以下幾個解決方法:
使用非AndroidX的版本插件。(優點就是見效快。缺點就是此插件后續的更新無法使用)
手動修改插件的沖突,因為Flutter插件的代碼是可以直接修改的,所以你可以手動修改掉這些沖突,統一插件的版本(優點就是可以使用***的版本。缺點就是這種方法首先麻煩,其次不利于團隊開發使用)
我偏好使用第二種,只要做好修改的相關記錄就行,算是一勞永逸。
10.Flutter Android 打包
打包本身流程沒有問題,配置好簽名文件,執行flutter build apk命令。但是發現打包后沒有將插件中的AndroidManifest.xml文件合并。比如我有使用image_picker插件,它的AndroidManifest.xml文件如下:
可以看到有權限的及Android 7.0FileProvider的聲明。諸如此類的信息沒有打包進去(但是引用xml中的flutter_image_picker_file_paths文件卻在),導致我實際使用這些功能時沒有反應,但是在平時的調試過程中卻是好的。
中間我發現打包后的App名稱也是之前的,懷疑是緩存問題,所以我手動刪除了項目根目錄的build與.gradle文件夾,重新打包就好了。所以打包后***檢查一下AndroidManifest.xml文件,避免此類緩存造成的問題。
11.其他
Container 功能強大,設置寬高、padding、margin、背景色、背景圖、圓角、陰影等都可以使用它。
有些widget 自帶padding 屬性,所以不必多套一層Padding部件。(比如ListView、GridView、Container、ScrollView、Button )
盡量使用const來定義常量。比如padding、color、style 這些地方:
class Colours { static const Color text_dark = Color(0xFF333333); } Padding( padding: const EdgeInsets.all(8.0), child: Text( "Test", style: TextStyle( fontSize: 26.0, color: Colours.text_dark ) ) )
4.Dart2中的new 關鍵字可選,所以就不要選了,哈哈!!
“Flutter開發中的技巧有哪些”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。