前一天的文章30天Flutter手滑系列 - 聊天室開發(Chat Room)(7),完成了TextField和IconButton的連動,今天來設計如何把訊息填入主畫面中。
首先來到ChatPageState,宣告一個陣列,準備用來儲存每次輸入的訊息內容。
class ChatPageState extends State<ChatPage> {
final TextEditingController _chatController = new TextEditingController();
final List<Widget> _message = [];   // 建立一個空陣列
...
}
然後到原本預留的Expanded內,新增一個ListView,將這個陣列綁定ListView上
Expanded(
  child: ListView.builder(
    padding: new EdgeInsets.all(8.0),
    itemBuilder: (context, index) => _messages[index],
    itemCount: _messages.length,
  ),
)
再來我們需要透過setState,把每次訊息輸入後的值存入這個陣列,使其能夠顯示在畫面上。
void _submitText(String text) {
    _chatController.clear();  // 清空controller資料
    setState(() {
      _messages.insert(0, Container(child: Text(text)));  // 把文字存入陣列0的位置
    });
  }
到目前為止,我們來看一下效果。
可以看到有兩個明顯問題:
- 訊息應該由下往上
- 一般來說,自己送出的訊息應該靠畫面右方。
第1點可以在ListView內加入reverse: true。
Expanded(
  child: ListView.builder(
    padding: new EdgeInsets.all(8.0),
    reverse: true,  // 加入reverse,讓它反轉
    itemBuilder: (context, index) => _messages[index],
    itemCount: _messages.length,
  ),
)
第2點可以在Text外層包一個Container,並用alignment: Alignment.centerRight,讓他靠右對齊。
_messages.insert(0, Container(child: Text(text), alignment: Alignment.centerRight));
修正後如下圖
在軟體開發過程中,如果是複雜的UI,很常會被設計成獨立的component,在這個APP中的訊息就是很單純的UI(指沒有邏輯行為),我們來將其模組化。
首先在/lib/下,建立一個components資料夾,並且新增一個messageBox.dart的檔案。
為了放置頭像在訊息右邊,我們用Row去排列,因此原先在Container的alignment就可以拿掉了。
要讓Row內的children都靠右,需要加入
mainAxisAlignment: MainAxisAlignment.end
然後再用一個Flexible包裝訊息長度,使其可以根據內容長度去彈性延伸長度。
在Flexible和Text之間再加入一個Container用來調整其樣式。
在Text中,為了怕訊息太長爆掉,可以加入overflow和maxLines
overflow: TextOverflow.ellipsis,
maxLines: 5,
小提示
在List、Column中,若要讓Text的換行起作用,需要加入Expanded或Flexible包裝

完整程式碼如下
import 'package:flutter/material.dart';
class MessageBox extends StatelessWidget {
  final String text;
  MessageBox({Key key, this.text}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.symmetric(vertical: 10.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          Flexible(
            child: Container(
              color: Colors.pink,
              padding: EdgeInsets.all(10.0),
              child: Text(text,
                  overflow: TextOverflow.ellipsis,
                  maxLines: 5,
                  style: TextStyle(fontSize: 18.0, color: Colors.white)),
            ),
          ),
          Icon(Icons.person, size: 32)
        ],
      ),
    );
  }
}
基本的對話介面已經完成了,剩下就是美化它或是加強功能。明天要回到頭痛的Firebase部分,希望能在完賽前把真正的即時訊息完成。