国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

flutter筆記8:實戰聊天頁面嵌入交互動畫和IOS風格適配

NervosNetwork / 2041人閱讀

摘要:當發送按鈕觸發事件后調用函數,在中執行了方法,此時根據中的變量變更重新渲染對象,然后大家就可以看到消息記錄框中底部新增了一行消息。

熟悉了flutter的各種控件和相互嵌套的代碼結構后,可以再加深一點難度:加入動畫特效。

雖然flutter的內置Metarial控件已經封裝好了符合其設計語言的動畫特效,使開發者節約了不少視覺處理上的精力,比如點擊或長按listTile控件時自帶水波紋動畫、頁面切換時切入向上或向下的動畫、列表上拉或下拉到盡頭有回彈波紋等。flutter也提供了用戶可自定義的動畫處理方案,使產品交互更加生動親切、富有情趣。

Flutter中封裝了包含有值和狀態(如向前,向后,完成和退出)的Animation對象。把Animation對象附加到控件中或直接監聽動畫對象屬性, Flutter會根據對Animation對象屬性的變化,修改控件的呈現效果并重新構建控件樹。

這次,敲一個APP的聊天頁面,試試加入Animation后的效果,再嘗試APP根據運行的操作系統進行風格適配。

第一步 構建一個聊天界面

先創建一個新項目:

flutter create chatPage

進入main.dart,貼入如下代碼:

import "package:flutter/material.dart";
//程序入口
void main() {
  runApp(new FriendlychatApp());
}

const String _name = "CYC";    //聊天帳號昵稱

class FriendlychatApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
    return new MaterialApp(        //創建一個MaterialApp控件對象,其下可塞入支持Material設計語言特性的控件
      title: "Friendlychat",
      home: new ChatScreen(),    //主頁面為用戶自定義ChatScreen控件
    );
  }
}

//單條聊天信息控件
class ChatMessage extends StatelessWidget {
  ChatMessage({this.text});
  final String text;
  @override
  Widget build(BuildContext context) {
    return new Container(
      margin: const EdgeInsets.symmetric(vertical: 10.0),
      child: new Row(                                   //聊天記錄的頭像和文本信息橫向排列
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          new Container(
            margin: const EdgeInsets.only(right: 16.0),
            child: new CircleAvatar(child: new Text(_name[0])),      //顯示頭像圓圈
          ),
          new Column(                                    //單條消息記錄,昵稱和消息內容垂直排列
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              new Text(_name, style: Theme.of(context).textTheme.subhead),    //昵稱
              new Container(
                margin: const EdgeInsets.only(top: 5.0),
                child: new Text(text),                    //消息文字
              ),
            ],
          ),
        ],
      ),
    );
  }
}

//聊天主頁面ChatScreen控件定義為一個有狀態控件
class ChatScreen extends StatefulWidget {
  @override
  State createState() => new ChatScreenState();   //ChatScreenState作為控制ChatScreen控件狀態的子類
}

//ChatScreenState狀態中實現聊天內容的動態更新
class ChatScreenState extends State {
  final List _messages = [];    //存放聊天記錄的數組,數組類型為無狀態控件ChatMessage
  final TextEditingController _textController = new TextEditingController();    //聊天窗口的文本輸入控件

  //定義發送文本事件的處理函數
  void _handleSubmitted(String text) {
    _textController.clear();        //清空輸入框
    ChatMessage message = new ChatMessage(    //定義新的消息記錄控件對象
      text: text,
    );
    //狀態變更,向聊天記錄中插入新記錄
    setState(() {
      _messages.insert(0, message);      //插入新的消息記錄
    });
  }

  //定義文本輸入框控件
  Widget _buildTextComposer() {
   return new Container(
       margin: const EdgeInsets.symmetric(horizontal: 8.0),  
       child: new Row(                    //文本輸入和發送按鈕都在同一行,使用Row控件包裹實現
          children: [
            new Flexible(                    
              child: new TextField( 
                controller: _textController,              //載入文本輸入控件
                onSubmitted: _handleSubmitted, 
                decoration: new InputDecoration.collapsed(hintText: "Send a message"),      //輸入框中默認提示文字
              ),
            ),
            new Container(
              margin: new EdgeInsets.symmetric(horizontal: 4.0),
              child: new IconButton(            //發送按鈕
                icon: new Icon(Icons.send),    //發送按鈕圖標
                onPressed: () => _handleSubmitted(_textController.text)),      //觸發發送消息事件執行的函數_handleSubmitted
           ),
         ]
       )
    );
  }
  //定義整個聊天窗口的頁面元素布局
  Widget build(BuildContext context) {
    return new Scaffold(              //頁面腳手架
      appBar: new AppBar(title: new Text("Friendlychat")),      //頁面標題
      body: new Column(             //Column使消息記錄和消息輸入框垂直排列
        children: [
        new Flexible(                     //子控件可柔性填充,如果下方彈出輸入框,使消息記錄列表可適當縮小高度
          child: new ListView.builder(        //可滾動顯示的消息列表
            padding: new EdgeInsets.all(8.0),
            reverse: true,                  //反轉排序,列表信息從下至上排列
            itemBuilder: (_, int index) => _messages[index],    //插入聊天信息控件
            itemCount: _messages.length,
          )
        ),
        new Divider(height: 1.0),      //聊天記錄和輸入框之間的分隔
        new Container(
          decoration: new BoxDecoration(
            color: Theme.of(context).cardColor),
          child: _buildTextComposer(),        //頁面下方的文本輸入控件
        ),
       ]
     ),
   );
  }
}

運行上面的代碼,可以看到這個聊天窗口已經生成,并且可以實現文本輸入和發送了:

如上圖標注的控件,最終通過放置在狀態對象ChatScreenState控件中的Scaffold腳手架完成安置,小伙伴可以輸入一些文本,點擊發送按鈕試試ListView控件發生的變化。

當發送按鈕IconButton觸發onPressed事件后調用_handleSubmitted函數,在_handleSubmitted中執行了setState()方法,此時flutter根據setState()中的變量_messages變更重新渲染_messages對象,然后大家就可以看到消息記錄框ListView中底部新增了一行消息。

由于ListView中的每一行都是瞬間添加完成,沒有過度動畫,使交互顯得非常生硬,因此向ListView中的每個Item的加入添加動畫效果,提升一下交互體驗。

第二步 消息記錄加入動效

改造ChatScreen控件

要讓主頁面ChatScreen支持動效,要在它的定義中附加mixin類型的對象TickerProviderStateMixin

class ChatScreenState extends State with TickerProviderStateMixin { // modified
  final List _messages = [];
  final TextEditingController _textController = new TextEditingController();
  ...
  }

向ChatMessage中植入動畫控制器控制動畫效果

class ChatMessage extends StatelessWidget {
  ChatMessage({this.text, this.animationController});        //new 加入動畫控制器對象
  final String text;
  final AnimationController animationController;
  @override
  Widget build(BuildContext context) {
    return new SizeTransition(             //new  用SizeTransition動效控件包裹整個控件,定義從小變大的動畫效果
      sizeFactor: new CurvedAnimation(                              //new  CurvedAnimation定義動畫播放的時間曲線
        parent: animationController, curve: Curves.easeOut),      //new  指定曲線類型
      axisAlignment: 0.0,                                           //new  對齊
      child: new Container(                                    //modified  Container控件被包裹到SizeTransition中
        margin: const EdgeInsets.symmetric(vertical: 10.0),
          child: new Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
            new Container(
              margin: const EdgeInsets.only(right: 16.0),
              child: new CircleAvatar(child: new Text(_name[0])),
            ),
            new Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
              new Text(_name, style: Theme.of(context).textTheme.subhead),
              new Container(
                margin: const EdgeInsets.only(top: 5.0),
                child: new Text(text),
              ),
            ],
          ),
        ],
      ),
    )                                                           //new
  );
}
}

修改_handleSubmitted()處理函數

由于ChatMessage對象的構造函數中添加了動畫控制器對象animationController,因此創建新ChatMessage對象時也需要加入animationController的定義:

void _handleSubmitted(String text) {
  _textController.clear();
  ChatMessage message = new ChatMessage(
    text: text,
    animationController: new AnimationController(                  //new
      duration: new Duration(milliseconds: 700),                   //new  動畫持續時間
      vsync: this,                                                 //new  默認屬性和參數
    ),                                                             //new
  );                                                               //new
  setState(() {
    _messages.insert(0, message);
  });
  message.animationController.forward();                           //new  啟動動畫
}

釋放控件

由于附加了動效的控件比較耗費內存,當不需要用到此頁面時最好釋放掉這些控件,Flutter會在復雜頁面中自動調用dispose()釋放冗余的對象,玩家可以通過重寫dispose()指定頁面中需要釋放的內容,當然由于本案例只有這一個頁面,因此Flutter不會自動執行到dispose()

@override
void dispose() {                                                   //new
  for (ChatMessage message in _messages)                           //new  遍歷_messages數組
    message.animationController.dispose();                         //new  釋放動效
  super.dispose();                                                 //new
} 

按上面的代碼改造完后,用R而不是r重啟一下APP,可以把之前沒有加入動效的ChatMessage對象清除掉,使整體顯示效果更和諧。這時候試試點擊發送按鈕后的效果吧~

可以通過調整在_handleSubmittedAnimationController對象的Duration函數參數值(單位:毫秒),改變動效持續時間。

可通過改變CurvedAnimation對象的curve參數值,改變動效時間曲線(和CSS的貝塞爾曲線類似),參數值可參考Curves

可以嘗試使用FadeTransition替代SizeTransition,試試動畫效果如何

實現了消息列表的滑動,但是這個聊天窗口還有很多問題,比如輸入框的文本只能橫向增加不會自動換行,可以空字符發送消息等,接下來就修復這些交互上的BUG,順便再復習下setState()的用法。

第三步 優化交互

杜絕發送空字符

TextField控件中的文本正在被編輯時,會觸發onChanged事件,我們通過這個事件檢查文本框中是否有字符串,如果沒有則點擊發送按鈕失效,如果有則可以發送消息。

class ChatScreenState extends State with TickerProviderStateMixin {
  final List _messages = [];
  final TextEditingController _textController = new TextEditingController();
  bool _isComposing = false;                                      //new  到ChatScreenState對象中定義一個標志位
  ...
}

向文本輸入控件_buildTextComposer中加入這個標志位的控制:

Widget _buildTextComposer() {
  return new IconTheme(
    data: new IconThemeData(color: Theme.of(context).accentColor),
    child: new Container(
      margin: const EdgeInsets.symmetric(horizontal: 8.0),
      child: new Row(
        children: [
          new Flexible(
            child: new TextField(
              controller: _textController,
              onChanged: (String text) {          //new  通過onChanged事件更新_isComposing 標志位的值
                setState(() {                     //new  調用setState函數重新渲染受到_isComposing變量影響的IconButton控件
                  _isComposing = text.length > 0; //new  如果文本輸入框中的字符串長度大于0則允許發送消息
                });                               //new
              },                                  //new
              onSubmitted: _handleSubmitted,
              decoration:
                  new InputDecoration.collapsed(hintText: "Send a message"),
            ),
          ),
          new Container(
            margin: new EdgeInsets.symmetric(horizontal: 4.0),
            child: new IconButton(
              icon: new Icon(Icons.send),
              onPressed: _isComposing
                  ? () => _handleSubmitted(_textController.text)    //modified
                  : null,                             //modified  當沒有為onPressed綁定處理函數時,IconButton默認為禁用狀態
            ),
          ),
        ],
      ),
    ),
  );
}

當點擊發送按鈕后,重置標志位為false:

void _handleSubmitted(String text) {
  _textController.clear();
  setState(() {                                                    //new  你們懂的
    _isComposing = false;                                          //new  重置_isComposing 值
  });                                                              //new
  ChatMessage message = new ChatMessage(
    text: text,
    animationController: new AnimationController(
      duration: new Duration(milliseconds: 700),
      vsync: this,
    ),
  );
  setState(() {
    _messages.insert(0, message);
  });
  message.animationController.forward();
}

這時候熱更新一下,再發送一條消息試試:

自動換行

當發送的文本消息超出一行時,會看到以下效果:

遇到這種情況,使用Expanded控件包裹一下ChatMessage的消息內容區域即可:

...

new Expanded(                                               //new  Expanded控件
  child: new Column(                                   //modified  Column被Expanded包裹起來,使其內部文本可自動換行
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      new Text(_name, style: Theme.of(context).textTheme.subhead),
      new Container(
        margin: const EdgeInsets.only(top: 5.0),
        child: new Text(text),
      ),
    ],
  ),
),                                                          //new

...
第四步 IOS和安卓風格適配

flutter雖然可以一套代碼生成安卓和IOS的APP,但是這兩者有著各自的設計語言:Material和Cupertino。因此為了讓APP能夠更好的融合進對應的系統設計語言,我們要對頁面中的控件進行一些處理。

引入IOS控件庫:

前面已經引入Material.dart控件庫,但還缺少了IOS的Cupertino控件庫,因此在main.dart的頭部中引入:

import "package:flutter/cupertino.dart"; 

定義Material和Cupertino的主題風格

Material為默認主題,當檢測到APP運行在IOS時使用Cupertino主題:

final ThemeData kIOSTheme = new ThemeData(    //Cupertino主題風格
  primarySwatch: Colors.orange,
  primaryColor: Colors.grey[100],
  primaryColorBrightness: Brightness.light,
);

final ThemeData kDefaultTheme = new ThemeData(    //默認的Material主題風格
  primarySwatch: Colors.purple,
  accentColor: Colors.orangeAccent[400],
);

根據運行的操作系統判斷對應的主題:

首先要引入一個用于識別操作系統的工具庫,其內的defaultTargetPlatform值可幫助我們識別操作系統:

import "package:flutter/foundation.dart";

到程序的入口控件FriendlychatApp中應用對應的操作系統主題:

class FriendlychatApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: "Friendlychat",
      theme: defaultTargetPlatform == TargetPlatform.iOS         //newdefaultTargetPlatform用于識別操作系統
        ? kIOSTheme                                              //new
        : kDefaultTheme,                                         //new
      home: new ChatScreen(),
    );
  }
}

頁面標題的風格適配

頁面頂部顯示Friendlychat的標題欄的下方,在IOS的Cupertino設計語言中沒有陰影,與下面的應用主體通過一條灰色的線分隔開,而Material則通過標題欄下方的陰影達到這一效果,因此將兩種特性應用到代碼中:

   // Modify the build() method of the ChatScreenState class.
   Widget build(BuildContext context) {
      return new Scaffold(
        appBar: new AppBar(
          title: new Text("Friendlychat"),
          elevation: Theme.of(context).platform == TargetPlatform.iOS ? 0.0 : 4.0),    //new  適配IOS的扁平化無陰影效果
          body: new Container(                    //modified    使用Container控件,方便加入主題風格裝飾
            child: new Column(                      //modified
              children: [
                new Flexible(
                  child: new ListView.builder(
                    padding: new EdgeInsets.all(8.0),
                    reverse: true,
                    itemBuilder: (_, int index) => _messages[index],
                    itemCount: _messages.length,
                  ),
                ),
              new Divider(height: 1.0),
              new Container(
              decoration: new BoxDecoration(color: Theme.of(context).cardColor),
              child: _buildTextComposer(),
            ),
          ],
        ),
          decoration: Theme.of(context).platform == TargetPlatform.iOS //new    加入主題風格
        ? new BoxDecoration(                                     //new
            border: new Border(                                  //new  為適應IOS加入邊框特性
              top: new BorderSide(color: Colors.grey[200]),      //new  頂部加入灰色邊框
            ),                                                   //new
          )                                                      //new
        : null),                                                 //modified  
      );
    }

發送按鈕的風格適配

發送按鈕在APP遇到IOS時,使用Cupertino風格的按鈕:

// Modify the _buildTextComposer method.

new Container(
   margin: new EdgeInsets.symmetric(horizontal: 4.0),
   child: Theme.of(context).platform == TargetPlatform.iOS ?  //modified
   new CupertinoButton(                              //new  使用Cupertino控件庫的CupertinoButton控件作為IOS端的發送按鈕
     child: new Text("Send"),                         //new
     onPressed: _isComposing                                  //new
         ? () =>  _handleSubmitted(_textController.text)      //new
         : null,) :                                           //new
   new IconButton(                                            //modified
       icon: new Icon(Icons.send),
       onPressed: _isComposing ?
           () =>  _handleSubmitted(_textController.text) : null,
       )
   ),

總結一下,為控件加入動畫效果,就是把控件用動畫控件包裹起來實現目的。動畫控件有很多種,今天只選用了一個大小變化的控件SizeTransition作為示例,由于每種動畫控件內部的屬性不同,都需要多帶帶配置,大家可參考官網了解這些動畫控件的詳情。

除此之外為了適應不同操作系統的設計語言,用到了IOS的控件庫和操作系統識別的控件庫,這是跨平臺開發中常用的功能。

好啦,flutter的入門筆記到本篇就結束了,今后將更新flutter的進階篇和實戰,由于近期工作任務較重,加上日常還有跟前端大神狐神學習golang的任務,以后的更新會比較慢,因此也歡迎大家到我的Flutter圈子中投稿,分享自己的成果,把這個專題熱度搞起來,趕上谷歌這次跨平臺的小火車,也可以加入flutter 中文社區(官方QQ群:338252156)共同成長,謝謝大家~

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/93428.html

相關文章

  • 跨平臺技術演進

    摘要:接下來,我將從原理優缺點等方面為大家分享跨平臺技術演進。小程序年是微信小程序飛速發展的一年,年,各大廠商快速跟進,已經有了很大的影響力。下面,我們以微信小程序為例,分析小程序的技術架構。 前言 大家好,我是simbawu ,@BooheeFE Team Leader,關于這篇文章,有問題歡迎來這里討論。 隨著移動互聯網的普及和快速發展,手機成了互聯網行業最大的流量分發入口。以及隨著5G...

    魏憲會 評論0 收藏0
  • 跨平臺技術演進

    摘要:接下來,我將從原理優缺點等方面為大家分享跨平臺技術演進。小程序年是微信小程序飛速發展的一年,年,各大廠商快速跟進,已經有了很大的影響力。下面,我們以微信小程序為例,分析小程序的技術架構。 前言 大家好,我是simbawu ,@BooheeFE Team Leader,關于這篇文章,有問題歡迎來這里討論。 隨著移動互聯網的普及和快速發展,手機成了互聯網行業最大的流量分發入口。以及隨著5G...

    MasonEast 評論0 收藏0

發表評論

0條評論

NervosNetwork

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<