Flutter 你需要知道的那些事 01

公眾號「AndroidTraveler」首發。
1. width 屬性
對於設定控制元件寬度填充父控制元件這件事情,在 Android 裡面,只需要設定 MATCH_PARENT 即可。
但是在 Flutter 裡面卻不是這樣,因為 Flutter 要具體的數值。
所以我們可以這樣考慮,假設我這個值非常大,比所有市面上的裝置寬度還要大,那麼是不是表現出來就是充滿父控制元件了。
所以這邊的做法是設定為無限,即 double.infinite
我們以一個常用場景來說明。
比如設定圖片填充螢幕寬度。
剛開始沒有設定的程式碼如下:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('My Flutter'), ), body: Center( child: Image.asset('assets/images/example.jpeg'), ), ) ); } }
效果:

可以看到沒有設定的情況下,顯示會根據圖片自身的寬高顯示。
這個時候如果設定 width 為無窮大,修改程式碼如下:
child: Image.asset('assets/images/example.jpeg', width: double.infinity,),
效果

什麼情況,沒起作用?

這個時候不要慌,我們來給大家分析分析。
以後大家遇到類似問題也可以這樣分析。
我們通過給 Image 外面套上一層 Container,然後設定背景顏色來對比一下。
程式碼如下:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('My Flutter'), ), body: Center( child: Container( color: Colors.blue, //left //child: Image.asset('assets/images/example.jpeg',), //right child: Image.asset('assets/images/example.jpeg', width: double.infinity,), ), ), )); } }
效果如下:

可以看到,設定寬度之後,Image 確實是填充了寬度,只不過由於圖片本身沒有那麼寬,因此看起來就以為是沒有起作用。
那麼如何讓圖片可以填充寬度呢?
這個就涉及到圖片的填充模式了。
2. fit 屬性
點選 Image 的 fit 屬性進入原始碼可以看到如下:
/// How to inscribe the image into the space allocated during layout. /// /// The default varies based on the other fields. See the discussion at /// [paintImage]. final BoxFit fit;
我們再點一下 BoxFit,可以看到如下:
/// How a box should be inscribed into another box. /// /// See also [applyBoxFit], which applies the sizing semantics of these values /// (though not the alignment semantics). enum BoxFit { /// Fill the target box by distorting the source's aspect ratio. /// ///  fill, /// As large as possible while still containing the source entirely within the /// target box. /// ///  contain, /// As small as possible while still covering the entire target box. /// ///  cover, /// Make sure the full width of the source is shown, regardless of /// whether this means the source overflows the target box vertically. /// ///  fitWidth, /// Make sure the full height of the source is shown, regardless of /// whether this means the source overflows the target box horizontally. /// ///  fitHeight, /// Align the source within the target box (by default, centering) and discard /// any portions of the source that lie outside the box. /// /// The source image is not resized. /// ///  none, /// Align the source within the target box (by default, centering) and, if /// necessary, scale the source down to ensure that the source fits within the /// box. /// /// This is the same as `contain` if that would shrink the image, otherwise it /// is the same as `none`. /// ///  scaleDown, }
相信大家看到原始碼的註釋應該很清楚每個值的意義了。
如果你還不清楚,可以點選註釋裡面對應的連結去檢視示意圖。
比如以我們這個實際應用場景填充寬度為例,那麼我們可以看到 fitWidth 應該是符合我們要求的,我們點選註釋的連結,跳轉可以看到圖片如下:

很形象的做了幾種情況的示意。我們設定了 Image 的 fit 屬性如下:
child: Image.asset('assets/images/example.jpeg', width: double.infinity, fit: BoxFit.fitWidth,),
效果:

可以看到已經滿足我們的需求了。
溫馨提示:測試完之後不要忘記去掉測試的 Container 以及對應顏色哦~
3. print
我們知道在 Android 裡面,當我們 try catch 之後,我們列印異常基本會寫出類似下面程式碼:
Log.e(TAG, "exception="+e);
在 Flutter 也有異常捕獲。
你可能會習慣的寫出如下程式碼:
print('exception='+e);
但是切記,不要使用上面的寫法。
因為當 e 為 null 時,上面的 print 不會執行列印。
這可能會誤導你。因為你在成功的時候加上列印語句,異常捕獲也加上列印語句。但是程式就是沒有列印。你就會覺得很奇怪。
實際上當 e 為 null 時,print 語句會報錯,+ 號連線的左右不能是 null,所以不會正常列印。因此請避免上面的寫法。可以用下面的替換寫法:
//替換寫法一 print('exception='); print(e); //替換寫法二 print('exception='+(e ?? '')); //替換寫法三 var printContent = e ?? ''; print('exception='+printContent);
4. GestureDetector
我們知道如果要給一個 Widget 增加點選事件,最簡單的方法就是套一層 GestureDetector。
但是有時候你這樣做了,卻發現有些“隱患”,或者說,有些你意料不到的事情。
這裡用一個場景來告訴你,你平時可能沒有發現的細節。
微博裡面有點贊這個小元件,我們寫下如下程式碼:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('My Flutter'), ), body: Row( children: <Widget>[ Image.asset('assets/images/2.0x/like.png', width: 20, height: 20,), SizedBox(width: 5,), Text('30') ], ), )); } }
效果如下:

假設我們要求給這個點贊元件加上點選事件,那麼我們直接給 Row 套上 GestureDetector Widget。
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('My Flutter'), ), body: GestureDetector( onTap: (){ print('onTap'); }, child: Row( children: <Widget>[ Image.asset('assets/images/2.0x/like.png', width: 20, height: 20,), SizedBox(width: 5,), Text('30') ], ), ), )); } }
點選點贊元件確實會列印 onTap,但是如果你點選了點贊圖示和數字中間的白色區域,你會發現點選事件沒有回撥,沒有列印。

這個時候有兩種解決方法:
1. 給空白元件設定 color 屬性,顏色值設定透明
對於 Container 設定的 padding 可以直接設定,對於我們這裡例子的 SizeBox 需要改為如下:
SizedBox(width: 15, child: Container(color: Colors.transparent,),),
為了方便測試,這邊將寬度改為 15。
所以對於設定 GestureDetector 的 Container,如果沒有設定 color 屬性,那麼點選空白不會回撥。
2. 設定 GestureDetector 的 behavior 屬性(推薦方式)
其實如果你需要空白區域也響應點選,只需要設定一下 GestureDetector 的 behavior 屬性即可。
behavior預設值為 HitTestBehavior.deferToChild ,我們這裡將其設定為 HitTestBehavior.translucent 。
程式碼如下:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('My Flutter'), ), body: GestureDetector( behavior: HitTestBehavior.translucent, onTap: (){ print('onTap'); }, child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Image.asset('assets/images/2.0x/like.png', width: 20, height: 20,), SizedBox(width: 15), Text('30') ], ), ), )); } }
這裡的點贊圖片我直接從網上獲取的,你測試可以用隨便一張圖片代替驗證。或者用兩個文字來驗證也是可以的。
