Flutter 你需要知道的那些事 01

AndroidTraveler發表於2019-05-05

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'),
          ),
        )
    );
  }
}
複製程式碼

效果:

Flutter 你需要知道的那些事 01

可以看到沒有設定的情況下,顯示會根據圖片自身的寬高顯示。

這個時候如果設定 width 為無窮大,修改程式碼如下:

child: Image.asset('assets/images/example.jpeg', width: double.infinity,),
複製程式碼

效果

Flutter 你需要知道的那些事 01

什麼情況,沒起作用?

Flutter 你需要知道的那些事 01

這個時候不要慌,我們來給大家分析分析。

以後大家遇到類似問題也可以這樣分析。

我們通過給 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,),
        ),
      ),
    ));
  }
}
複製程式碼

效果如下:

Flutter 你需要知道的那些事 01

可以看到,設定寬度之後,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.
  ///
  /// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_fill.png)
  fill,

  /// As large as possible while still containing the source entirely within the
  /// target box.
  ///
  /// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_contain.png)
  contain,

  /// As small as possible while still covering the entire target box.
  ///
  /// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_cover.png)
  cover,

  /// Make sure the full width of the source is shown, regardless of
  /// whether this means the source overflows the target box vertically.
  ///
  /// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_fitWidth.png)
  fitWidth,

  /// Make sure the full height of the source is shown, regardless of
  /// whether this means the source overflows the target box horizontally.
  ///
  /// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_fitHeight.png)
  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.
  ///
  /// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_none.png)
  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`.
  ///
  /// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_scaleDown.png)
  scaleDown,
}
複製程式碼

相信大家看到原始碼的註釋應該很清楚每個值的意義了。

如果你還不清楚,可以點選註釋裡面對應的連結去檢視示意圖。

比如以我們這個實際應用場景填充寬度為例,那麼我們可以看到 fitWidth 應該是符合我們要求的,我們點選註釋的連結,跳轉可以看到圖片如下:

Flutter 你需要知道的那些事 01

很形象的做了幾種情況的示意。我們設定了 Image 的 fit 屬性如下:

child: Image.asset('assets/images/example.jpeg', width: double.infinity, fit: BoxFit.fitWidth,),
複製程式碼

效果:

Flutter 你需要知道的那些事 01

可以看到已經滿足我們的需求了。

溫馨提示:測試完之後不要忘記去掉測試的 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')
        ],
      ),
    ));
  }
}
複製程式碼

效果如下:

Flutter 你需要知道的那些事 01

假設我們要求給這個點贊元件加上點選事件,那麼我們直接給 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,但是如果你點選了點贊圖示和數字中間的白色區域,你會發現點選事件沒有回撥,沒有列印。

Flutter 你需要知道的那些事 01

這個時候有兩種解決方法:

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')
          ],
        ),
      ),
    ));
  }
}
複製程式碼

這裡的點贊圖片我直接從網上獲取的,你測試可以用隨便一張圖片代替驗證。或者用兩個文字來驗證也是可以的。

Flutter 你需要知道的那些事 01

相關文章