Flutter 的基本控制項

来源:https://www.cnblogs.com/shsuper/archive/2019/10/12/11662601.html
-Advertisement-
Play Games

文本控制項 Text 支持兩種類型的文本展示,一個是預設的展示單一樣式文本 Text,另一個是支持多種混合樣式的富文本 Text.rich。 單一樣式文本 Text 單一樣式文本 Text 的初始化,是要傳入需要展示的字元串。而這個字元串的具體展示效果,受構造函數中的其他參數控制。這些參數大致可以分為 ...


文本控制項

Text 支持兩種類型的文本展示,一個是預設的展示單一樣式文本 Text,另一個是支持多種混合樣式的富文本 Text.rich。

單一樣式文本 Text

單一樣式文本 Text 的初始化,是要傳入需要展示的字元串。而這個字元串的具體展示效果,受構造函數中的其他參數控制。這些參數大致可以分為兩類:

  • 控制整體文本佈局的參數,如文本對齊方式 textAlign、文本排版方向 textDirection,文本顯示最大行數 maxLines、文本截斷規則 overflow 等等,這些都是構造函數中的參數;
  • 控制文本展示樣式的參數,如字體名稱 fontFamily、字體大小 fontSize、文本顏色 color、文本陰影 shadows 等等,這些參數被統一封裝到構造函數中的參數 style 中。

示例代碼 - 定義了一段劇中佈局、20號紅色粗體展示樣式的字元串:

Text(
    '文本是視圖系統中的常見空間,用來顯示一段特定樣式的字元串,就比如 Android 里的 TextView,或是 iOS 中的 UILabel。',
    textAlign: TextAlign.center, // 居中顯示
    style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: Colors.red),// 20 號,紅色粗體展示
);

支持多種混合樣式的富文本 Text.rich

混合展示樣式與單一樣式的關鍵區別在於分片,即如何把一段字元串分為幾個片段來管理,給每個片段單獨設置樣式。在 Flutter 中可以使用 TextSpan。

TextSpan 定義來一個字元串片段該如何控制其展示樣式,而將這些有著獨立展示樣式的字元串組裝在一起,則可以支持混合樣式的富文本展示。

示例代碼 - 分別定義黑色與紅色兩種展示樣式

TextStyle blackStyle = TextStyle(fontWeight: FontWeight.normal, fontSize: 20, color: Colors.black); // 黑色樣式
    TextStyle redStyle = TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: Colors.red); // 紅色樣式
    Text.rich(
      TextSpan(
        children: <TextSpan>[
          TextSpan(text: '文本是視圖系統中的常見空間,用來顯示一段特定樣式的字元串,就比如', style: redStyle), // 第 1 個片段,紅色樣式
          TextSpan(text: 'Android', style: blackStyle), // 第 1 個片段,黑色樣式
          TextSpan(text: '中的', style: redStyle), // 第 1 個片段,紅色樣式
          TextSpan(text: 'TextView', style: blackStyle), // 第 1 個片段,黑色樣式
        ]
      ),
    textAlign: TextAlign.center,
);

圖片

在 Flutter 中有多張方式,用來載入不同形式、支持不同格式的圖片:

  • 載入本地資源圖片,如 Image.asset('images/log.png');
  • 載入本地(File 文件)圖片,如 Image.file(new File('/storage/xxx/xxx/test.jpg'));
  • 載入網路圖片,如 Image.network('http://xxx/xxx/test.gif')。

除了可以根據圖片的顯示方式設置不同的圖片源之外,圖片的構造方法還提供了填充模式 fit、拉伸模式 centerSlice、重覆模式 repeat 等屬性,可以針對圖片與目標區域的寬高比差異制定排版模式。

FadeInImage 控制項

在載入網路圖片的時候,為了提升用戶的等待體驗,往往會加入占點陣圖、載入動畫等元素,但是預設的 Image.network 構造方法並不支持這些高級功能,這時候 FadeInImage 控制項就派上用場了。

FadeInImage 控制項提供了圖片占位的功能,並且支持在圖片載入完成時淡入淡出的視覺效果。此外,由於 Image 支持 gif 格式,甚至還可以將一些炫酷的載入動畫作為占點陣圖。

示例代碼 - loading 的 gif 作為占點陣圖展示:

FadeInImage.assetNetwork(
    placeholder: 'asssets/loading.gif', // gif 占位
    image: 'https://xxx/xxx/xxx.jpg',
    fit: BoxFit.cover, // 圖片拉伸模式
    width: 200,
    height: 200,
);

Image 控制項需要根據圖片資源非同步載入的情況,決定自身的顯示效果,因此是一個 StatefulWidget。圖片載入過程由 ImageProvider 觸發,而 ImageProvider 表示非同步獲取圖片數據的操作,可以從資源、文件和網路等不同的渠道獲取圖片。

首先,ImageProvider 根據 _imageSate 中傳遞的圖片配置生成對應的圖片緩存 key;然後,去 ImageCache 中查找是否有對應的圖片緩存,如果有,則通知 _ImageState 刷新 UI;如果沒有,則啟動 ImageStream 開始非同步載入,載入完畢後,更新緩存;最後通知 _imageSate 刷新 UI。

值得註意的是,ImageCache 使用 LRU(Least Recently Used,最近最少使用)演算法進行緩存更新策略,並且預設最多存儲 1000 張圖片,最大緩存限製為 100 MB,當限定的空間已經存滿數據時,把最久沒有被訪問到的圖片清除。圖片 緩存只會在運行期間生效,也就是只緩存在記憶體中。如果想要支持緩存到文件系統,可以使用第三方的 CachedNetworkImage 控制項。

按鈕

通過按鈕,可以相應用戶的交互事件。Flutter 提供了三個基本的按鈕空間,即 FloatingActionButton、FlatButton 和 RaisedButton。

  • FloatingActionButton:一個圓形的按鈕,一般出現在屏幕內容的前面,用來處理界面中最常用、最基礎的用戶動作。
  • RaisedButton:凸起的按鈕,預設帶有灰色背景,被點擊後灰色背景會加深。
  • FlatButton:扁平化的按鈕,預設透明背景,被點擊後呈現灰色背景。

既然是按鈕,因此除了控制基本樣式之外,還需要響應用戶點擊行為。這就對應著按鈕空間中的兩個最重要的參數:

  • onPressed 參數用於設置點擊回調,告訴 Flutter 在按鈕被點擊時通知我們。如果 onPressed 參數為空,則按鈕會處於禁用狀態,不響應用戶點擊。
  • child 參數用於設置按鈕的內容,告訴 Flutter 控制項應該長成什麼樣,也就是控制著按鈕控制項的基本樣式。child 可以接收任意的 Widget。

除此之外,還可以進行樣式定製(以 FlatButton 為例):

FlatButton(
    color: Colors.yellow, // 設置背景色為黃色
    shape: BeveledRectangleBorder(borderRadius: BorderRadius.circular(20.0)), // 設置斜角矩形邊框
    colorBrightness: Brightness.light, // 確保文字按鈕為深色
    onPressed: () => print('FlatButton pressed'),
    child: Row(children: <Widget>[Icon(Icons.add), Text("Add")],),
);

ListView

若相關基本元素的排列佈局超過屏幕顯示尺寸(即超過一屏)時,就需要引入列表控制項來展示視圖的完整內容,並根據元素的多少進行自適應滾動展示。

這樣的需求,在 Android 中是由 ListView 或 RecyclerView 實現的,在 iOS 中是用 UITableView 實現的;而在 Flutter 中,實現這種需求的則是列表空間 ListView。

在 Flutter 中,ListView 可以沿著一個方向(垂直或水平方向)來排列其所有子 Widget,因此常用於需要展示一組連續視圖元素的場景,比如通訊錄、優惠劵、商家列表等。

ListView 提供了一個預設構造函數 ListView,可以通過設置它的 children 參數,很方便地將所有的子 Widget 包含到 ListView 中。

但是,這種創建方式要求提前將所有的子 Widget 一次性創建好,而不是等到它們真正在屏幕上需要顯示時才創建,所以有一個很明顯的缺點,就是性能不好。因此,這種方式僅適用於列表中含有少量元素的場景。

ListView(
    scrollDirection: Axis.horizontal, // 設置滾動方向
    children: <Widget>[
        // 設置 ListTile 組件的標題與圖標
        ListTile(leading: Icon(Icons.map), title: Text('Map'),),
        ListTile(leading: Icon(Icons.mail), title: Text('Mail'),),
        ListTile(leading: Icon(Icons.message), title: Text('Message'),),
    ],
);

**ListView 的另外一個構造函數 ListView.builder,則適用於子 Widget 比較多的場景。這個構造函數有兩個關鍵參數:

  • itemBuilder,是列表項的創建方法。當列表滾動到相應位置時,ListView 會調用該方法創建對應的子 Widget。
  • itemCount,表示列表項的數量,如果為空,則表示 ListView 為無限列表。

具體用法如下,定義一個擁有 100 個列表元素的 ListView:

ListView.builder(
    itemCount: 100, // 元素個數
    itemExtent: 50.0, // 列表項高度
    itemBuilder: (BuildContext context, int index) => ListTile(title: Text("title $index"), subtitle: Text("body $index"),),
);

需要註意的是,itemExtent並不是一個必填參數。但,對於定高的列表項元素,強烈建議提前設置好這個參數的值。

因為如果這個參數為 null,ListView 會動態地根據子 Widget 創建完成的結果,決定自身的視圖高度,以及子 Widget 在 ListView 中的相對位置。在滾動發生變化而列表項又很多時,這樣的計算就會非常頻繁。

在 ListView 中,有兩種方式支持分割線:

  • 一種是,在 itemBuilder 中,根據 index 的值動態創建分割線,也就是將分割線視為列表項的一部分;
  • 另一種是,使用 ListView 的另一個構造方法 ListView.separated,單獨設置分割線的樣式。

總結 ListView 常見的構造方法及適用場景如下:

構造函數名特點適用場景使用頻次
ListView 一次性創建好全部子 Widget 適用於展示少量連續子 Widget 的場景
List.builder 提供了子 Widget 創建方法,僅在需要展示時才創建 適用於子 Widget 較多,且視覺效果呈現某種規律性的場景
ListView.separated 與 ListView.builder 類似,並提供了自定義分割線的功能 與 ListView.builder 場景類似

CustomScrollView

ListView 實現了單一視圖下可滾動 Widget 的交互模式,同時也包含了 UI 顯示相關的控制邏輯和佈局模型。但是,對於某些特殊交互場景,比如多個效果聯動、嵌套滾動、精細滑動、視圖跟隨手勢操作等,還需要嵌套多個 ListView 來實現。這時,各自視圖的滾動和佈局模型就是相互獨立、分離的,就很難保證整個頁面統一一致的滑動效果。

在 Flutter 中有一個專門的控制項 CustomScrollView,用來處理多個需要自定義滾動效果的 Widget。在 CustomScrollView 中,這些彼此獨立的、可滾動的 Widget 被統稱為 Sliver。

比如,ListView 的 Sliver 實現為 SliverList,AppBar 的 Sliver 實現為 SliverAppBar。這些 Sliver 不再維護各自的滾動狀態,而是交由 CustomScrollView 統一管理,最終實現滑動效果的一致性。

可以通過一個滾動視差的例子,演示 CustomScrollView 的使用方法。

視差滾動是指讓多層背景以不同的速度移動,在形成立體滾動效果的同時,還能保證良好的視覺體驗。作為移動應用交互設計的熱點趨勢,越來越多的移動應用使用來這項技術。

以一個有著封面頭圖的列表為例,封面頭圖和列表這兩層視圖的滾動聯動起來,當用戶滾動列表時,頭圖會根據用戶的滾動手勢,進行縮小和展開。

經過分析得出,要實現這樣的需求,需要兩個 Sliver:作為頭圖的 SliverAppBar,作為列表的 SliverList。思路如下:

  • 在創建 SliverAppBar 時,把 flexibleSpace 參數設置為懸浮頭圖背景。flexibleSpace 可以讓背景圖顯示在 AppBar 下方,高度和 SliverAppBar 一樣;
  • 而在創建 SliverList 時,通過 SliverChildBuilderDelegate 參數實現列表項元素的創建;
  • 最後,將它們一併交由 CustomScrollView 的 slivers 參數統一管理。

具體的示例代碼如下:

    CustomScrollView (
      slivers: <Widget>[
        SliverAppBar( // SliverAppBar 作為頭圖控制項
          title: Text('CustomScrollView Demo'), // 標題
          floating: true, // 設置懸浮樣式
          flexibleSpace: Image.network("https://xx.jpg", fit: BoxFit.cover,), // 設置懸浮頭圖背景
          expandedHeight: 300, // 頭圖控制項高度
        ),
        SliverList( // SliverList 作為列表控制項
          delegate: SliverChildBuilderDelegate(
              (context, index) => ListTile(title: Text('Item #$index'),), //列表項創建方法
              childCount: 100, // 列表元素個數
          ),
        )
      ],
    );

ScrollController 與 ScrollNotification

使用 ScrollController 進行滾動信息的監聽,以及相應的滾動控制;ScrollNotifiCation 通知進行滾動事件的獲取。

在 Flutter 中,因為 Widget 並不是渲染到屏幕的最終視覺元素(RenderObject 才是),所以無法像原生的 Android 或 iOS 系統那樣,向持有的 Widget 對象獲取或者設置最終渲染相關的視覺信息,而必須通過對應的組件控制器才能實現。

ListView 的組件控制器則是 ScrollController,我們可以通過它來獲取視圖的滾動信息,更新視圖的滾動位置。

一般而言,獲取視圖的滾動信息往往是為了進行界面的狀態控制,因此 ScrollController 的初始化、監聽及銷毀需要與 StatefulWidget 的狀態保持同步。

代碼示例所示,聲明一個有著 100 個元素的列表項,當滾動視圖到特定位置後,用戶可以點擊按鈕返回列表頂部:

  • 首先,在 State 的初始化方法里,創建了 ScrollController,並通過 _controller.addListener 註冊了滾動監聽方法回調,根據當前視圖的滾動位置,判斷當前是否需要展示“Top”按鈕。
  • 隨後,在視圖構建方法 build 中,將 ScrollController 對象與 ListView 進行了關聯,並且在 RaisedButton 中註冊了對應的回調方法,可以在點擊按鈕時通過 _controller.animateTo 方法返回列表頂部。
  • 最後,在 State 的銷毀方法中,對 ScrollController 進行了資源釋放。
class _MyAppState extends State<MyApp> {

  ScrollController _controller; // ListView 控制器
  bool isToTop = false; // 標示目前是否需要啟用 "Top" 按鈕
  @override
  void initState() {

    _controller = ScrollController();
    _controller.addListener(() { // 為控制器註冊滾動監聽方法

      if (_controller.offset > 1000) { // 如果 ListView 已經向下滾動了 1000,則啟用 Top 按鈕
        setState(() {
          isToTop = true;
        });
      }
      else if (_controller.offset < 300) { // 如果 ListView 向下滾動距離不足 300,則禁用 Top 按鈕
        setState(() {
          isToTop = false;
        });
        super.initState();
      }
    });
  }

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Scroll Controller Widget"),),
      body: Column(
        children: <Widget>[
          Container(
            height: 40.0,
            child: RaisedButton(onPressed: (isToTop ? () {
              if (isToTop) {
                _controller.animateTo(.0, duration: Duration(milliseconds: 200), curve: Curves.ease);
              }
            } : null), child: Text("Top"),),
          ),
          Expanded(
            child: ListView.builder(
                controller: _controller, // 初始化傳入控制器
                itemCount: 100, // 列表元素總和
                itemBuilder: (context, index) => ListTile(title: Text("Index : $index"),) // 列表項構造方法
            ),
          )
        ],
      ),
    );
  }
  
  @override
  void dispose() {
    _controller.dispose(); // 銷毀控制器
    super.dispose();
  }
}

在 Flutter 中, ScroNotification 通知的獲取是通過 NotificationListener 來實現的。與 ScrollController 不同的是,NotificationListener 是一個 Widget,為了監聽滾動類型的事件,我們需要將 NotificationListener 添加為 ListView 的父容器,從而捕獲 ListView 中的通知。而這些通知,需要通過 onNotification 回調函數實現監聽邏輯:

  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ScrollController Demo',
      home: Scaffold(
        appBar: AppBar(title: Text('ScrollController Demo')),
        body: NotificationListener<ScrollNotification>(
          onNotification: (scrollNotification) {
            if (scrollNotification is ScrollStartNotification) { // 開始滾動
              
            }
            else if ( scrollNotification is ScrollUpdateNotification) { // 滾動位置更新

            }
            else if (ScrollStartNotification is ScrollEndNotification) { // 滾動結束
              
            }
          }, 
            child: ListView.builder(itemBuilder: (context, index) => ListTile(title: Text("Index : $index"),));
        ), 
          
      ),
        
    );
    
  }

相比於 ScrollController 只能和具體的 ListView 關聯後才可以監聽到滾動信息;通過 NotificationListener 則可以監聽其子 Widget 中的任意 ListView,不僅可以得到這些 ListView 的當前滾動位置信息,還可以獲取當前的滾動事件信息。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.創建學生管理系統資料庫xscj create detabase 資料庫名; 2.打開資料庫 use 資料庫名; //創建資料庫之後,該資料庫不會自動成為當前資料庫需要用use來指定 3.創建表名 4.在表xs中增加“獎學金等級”列 ,並刪除表中的“姓名”列 alter table 表名 add ...
  • 轉自:http://www.maomao365.com/?p=10025 摘要: 下文使用sql腳本生成中文名字的方法分享,如下所示: 實驗環境:sql server 2008 R2 在工作中,我們有時需要批量生成隨機姓名,下麵將講述使用sql腳本生成隨機"名字"的方法分享,如下所示:實現思路: 1 ...
  • 前面的文章中我們講道,像趣頭條類的APP對於收徒和閱讀行為給予用戶現金獎勵的方式勢必會受到大量羊毛黨黑產的註意,其實單個用戶能薅到的錢是沒有多少的,為了達到利益最大化,黑產肯定會利用各種手段構建大量賬號來薅APP運營企業的羊毛,因為收徒的獎勵遠高於閱讀,所以賺取收徒獎勵就成了最嚴重的薅羊毛手段。前文 ...
  • 早上剛睜眼,看到了一堆資料庫告警的簡訊,其中一個內容如下: 眼看這是剛從其他DBA交接過來的資料庫,不敢怠慢,立馬起來查看從庫日誌信息如下: 即非正常停止。 再登錄主庫機器查看主庫錯誤日誌,信息如下 從主庫日誌可以看出,2個從庫是主庫主動斷開的,而給出的信息也指出了原因failed on flush ...
  • Microsoft SQL Server 2012安裝說明 環境:Windows8, Windows7, WinVista, Win2003, WinXP Microsoft SQL Server 2012是一款強大的MySQL資料庫管理和開發工具。新版的Microsoft SQL Server 2 ...
  • 1. Objective C語言使用的是"消息結構"而非"函數調用"。 "消息結構"和"函數調用"之間的區別 "消息結構"的語言: 運行時由運行環境決定所應執行的代碼 "函數調用"的語言: 由編譯器決定 記憶體模型:Objective C語言中的指針是用來指示對象的。 Objective C為C語言添 ...
  • flutterBoost使用筆記 新一代Flutter-Native混合解決方案。 FlutterBoost是一個Flutter插件,它可以輕鬆地為現有原生應用程式提供Flutter混合集成方案。FlutterBoost的理念是將Flutter像Webview那樣來使用。在現有應用程式中同時管理Na ...
  • 如需轉載,請註明出處:Flutter學習筆記(29)--Flutter如何與native進行通信 前言:在我們開發Flutter項目的時候,難免會遇到需要調用native api或者是其他的情況,這時候就需要處理Flutter與native的通信問題,一般常用的Flutter與native的通信方式 ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...