Flutter 案例学习之:构建漂亮的 UI

效果图

GitHub:https://github.com/happy-python/flutter_demos/tree/master/friendlychat

原文链接

lib/main.dart

import 'package:flutter/material.dart';

void main() => runApp(FriendlychatApp());

class FriendlychatApp extends StatelessWidget {
  // 定义全局主题
  final ThemeData iOSTheme = ThemeData(
    primarySwatch: Colors.blue,
  );

  final ThemeData defaultTheme = ThemeData(
    primarySwatch: Colors.orange,
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Friendlychat',
      // 使用 Theme.of(context).platform 获取平台
      theme: Theme.of(context).platform == TargetPlatform.iOS
          ? iOSTheme
          : defaultTheme,
      home: ChatScreen(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class ChatScreen extends StatefulWidget {
  @override
  State createState() => ChatScreenState();
}

class ChatScreenState extends State with TickerProviderStateMixin {
  // 使用 TextEditingController 来读取输入字段的内容,并在发送文本消息后清除字段
  final TextEditingController _textController = TextEditingController();
  final List _messages = [];
  bool _isComposing = false;

  void _handleSubmitted(String text) {
    if (text.length > 0) {
      final ChatMessage message = ChatMessage(
        text: text,
        animationController: AnimationController(
          vsync: this,
          duration: Duration(
            milliseconds: 300,
          ),
        ),
      );
      // 调用 setState 修改 _messages 达到让框架重建 UI 的目的
      // 只应在 setState 中执行同步操作,否则框架可能会在操作完成之前重建 widgets
      setState(() {
        _messages.insert(0, message);
        _isComposing = false;
      });
      message.animationController.forward();
      _textController.clear();
    }
  }

  Widget _buildTextComposer() {
    /// 使用 IconTheme + _isComposing 来动态控制 IconButton 的外观和行为
    /// 如果用户输入了文本内容,_isComposing 为 true,按钮的颜色被设置为 Theme.of(context).accentColor
    /// 如果用户没有输入文本内容,_isComposing 为 false,onPressed 属性被置为 null,禁用发送按钮,框架会自动将按钮的颜色更改为 Theme.of(context).disabledColor
    return IconTheme(
      data: IconThemeData(
        // 获取全局设置的颜色,避免硬编码
        color: Theme.of(context).accentColor,
      ),
      child: Container(
        // 水平边距
        margin: EdgeInsets.symmetric(horizontal: 8.0),
        child: Row(
          children: [
            Flexible(
              child: TextField(
                controller: _textController,
                onSubmitted: _handleSubmitted,
                decoration: InputDecoration.collapsed(
                  // 提示文本
                  hintText: "Send a message",
                ),
                onChanged: (String text) {
                  setState(() {
                    _isComposing = text.length > 0;
                  });
                },
              ),
            ),
            Container(
              margin: EdgeInsets.symmetric(horizontal: 4.0),
              child: IconButton(
                icon: Icon(
                  Icons.send,
                ),
                onPressed: _isComposing
                    ? () => _handleSubmitted(_textController.text)
                    : null,
              ),
            ),
          ],
        ),
      ),
    );
  }

  /// 处理动画控制器在不再需要时释放资源是一种很好的做法
  @override
  void dispose() {
    for (var message in _messages) {
      message.animationController.dispose();
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Friendlychat"),
        // 阴影效果
        elevation: Theme.of(context).platform == TargetPlatform.iOS ? 0.0 : 4.0,
      ),
      body: Container(
        child: Column(
          children: [
            Flexible(
              child: ListView.builder(
                itemCount: _messages.length,
                itemBuilder: (BuildContext context, int index) =>
                    _messages[index],
                // 使 ListView 从屏幕底部开始显示
                reverse: true,
                padding: EdgeInsets.all(8.0),
              ),
            ),
            Divider(
              height: 1.0,
            ),
            Container(
              decoration: BoxDecoration(
                color: Theme.of(context).cardColor,
              ),
              child: _buildTextComposer(),
            ),
          ],
        ),
        decoration: Theme.of(context).platform == TargetPlatform.iOS
            ? BoxDecoration(
                border: Border(
                  top: BorderSide(
                    color: Colors.grey[300],
                  ),
                ),
              )
            : null,
      ),
    );
  }
}

class ChatMessage extends StatelessWidget {
  final String text;
  final AnimationController animationController;

  ChatMessage({this.text, this.animationController});

  @override
  Widget build(BuildContext context) {
    const String _name = '帅哥';
    // 动画效果
    return FadeTransition(
      opacity: CurvedAnimation(
        parent: animationController,
        curve: Curves.bounceIn,
      ),
      child: Container(
        // 垂直边距
        margin: EdgeInsets.symmetric(vertical: 10.0),
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Container(
              // 右边距
              margin: EdgeInsets.only(right: 16.0),
              child: CircleAvatar(
                child: Text(_name[0]),
              ),
            ),
            // 使用 Expanded 包裹长文本
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    _name,
                    // 获取全局设置的文本主题,避免硬编码
                    style: Theme.of(context).textTheme.subhead,
                  ),
                  Container(
                    margin: EdgeInsets.only(top: 5.0),
                    child: Text(text),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

你可能感兴趣的:(Flutter 案例学习之:构建漂亮的 UI)