Flutter Chat App: 添加消息删除功能

本篇博客将讲解如何在 Flutter Chat App 中为每条消息添加一个删除按钮,实现滑动删除功能。

实现思路

可以在每条消息的外层包裹一个 Dismissible 组件,然后设置 onDismissed 属性为删除该条消息的方法。

代码实现

import 'dart:async';
import 'dart:convert';

import 'package:animate_do/animate_do.dart';
import 'package:animated_text_kit/animated_text_kit.dart';
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:muse_nepu_course/global.dart';
import 'package:muse_nepu_course/home.dart';
import 'package:muse_nepu_course/service/api_service.dart';
import 'package:muse_nepu_course/theme/color_schemes.g.dart';

class ChatPage extends StatefulWidget {
  @override
  _ChatPageState createState() => _ChatPageState();
}

var _filteredStringList = [];
var _stringList = [];
int _selectedIndex = -1;
String? _selectedValue;
//从assets/prompts.json中读取数据,并转换为List<Map<String, String>>
void loadprompts() async {
  String prompt = await rootBundle.loadString('assets/prompts.json');
  _stringList = json.decode(prompt);
  _filteredStringList = List.from(_stringList);
}

class _ChatPageState extends State<ChatPage>
    with TickerProviderStateMixin, WidgetsBindingObserver {
  final List<Map<String, String>> _messages = [];
  final TextEditingController _controller = TextEditingController();
  final ScrollController _scrollController = ScrollController();
  final FocusNode _focusNode = FocusNode();

  String xdata = '';
  late AnimationController controller;

  bool _sending = false;
  bool _showAnimation = true;
  bool _isAtBottom = true;

  int _maxLines = 1; // 最多显示的行数

  @override
  void initState() {
    super.initState();
    Global.bottombarheight = 60;
    _messages.clear();
    Global.messages_pure = '';
    ApiService().active_chatgpt();
    controller = AnimationController(
      duration: const Duration(milliseconds: 500),
      vsync: this,
    );
    _focusNode.requestFocus();

    loadprompts(); // 加载提示语句

    WidgetsBinding.instance!.addObserver(this);
  }

  @override
  void didChangeMetrics() {
    super.didChangeMetrics();
    if (_isAtBottom) {
      WidgetsBinding.instance!.addPostFrameCallback((_) {
        _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        theme: ThemeData(useMaterial3: true, colorScheme: lightColorScheme),
        darkTheme: ThemeData(useMaterial3: true, colorScheme: darkColorScheme),
        home: Scaffold(
          appBar: AppBar(
            backgroundColor: Global.home_currentcolor,
            title: Text(
              '双击消息复制',
              style: TextStyle(color: Colors.white),
            ),
            leading: IconButton(
              icon: Icon(Icons.arrow_back),
              onPressed: () {
                // Navigator.push(
                //     context, MaterialPageRoute(builder: (context) => HomePage()));
                //回到上一级
                Navigator.pop(context);
              },
            ),
            actions: [
              IconButton(
                icon: Icon(Icons.settings),
                onPressed: () {
                  showDialog(
                      context: context,
                      builder: (context) {
                        return Scaffold(
                            appBar: AppBar(
                              backgroundColor: Global.home_currentcolor,
                              title: Text('自动注入消息'),
                            ),
                            body: ListView.builder(
                              itemCount: _filteredStringList.length,
                              itemBuilder: (BuildContext context, int index) {
                                String act = _filteredStringList[index]['act']!;
                                String prompt =
                                    _filteredStringList[index]['prompt']!;
                                return RadioListTile(
                                  title: Text('行为: $act'),
                                  subtitle: Text('语句: $prompt'),
                                  value: index,
                                  groupValue: _selectedIndex,
                                  onChanged: (int? value) {
                                    setState(() {
                                      _selectedIndex = value!;
                                      _selectedValue =
                                          _filteredStringList[_selectedIndex]['act'];
                                      //发送消息

                                      _controller.text =
                                          _filteredStringList[_selectedIndex]['prompt'];
                                      //返回主页面
                                      Navigator.pop(context);
                                    });
                                  },
                                );
                              },
                            ));
                      });
                },
              ),
            ],
          ),
          body: Container(
            child: Column(
              children: [
                Expanded(
                  child: Container(
                    padding: EdgeInsets.all(12.0),
                    child: _messages.isEmpty
                        ? FadeInUp(
                            child: AnimatedOpacity(
                            opacity: _showAnimation ? 1.0 : 0.0,
                            duration: Duration(milliseconds: 500),
                            child: Center(
                              child: DefaultTextStyle(
                                style: TextStyle(
                                  fontSize: 24.0,
                                  fontWeight: FontWeight.bold,
                                ),
                                child: AnimatedTextKit(
                                  animatedTexts: [
                                    ColorizeAnimatedText(
                                      'chatgpt',
                                      colors: [
                                        Colors.purple,
                                        Colors.blue,
                                        Colors.yellow,
                                        Colors.red,
                                      ],
                                      textStyle: TextStyle(fontSize: 50.0),
                                    ),
                                  ],
                                  isRepeatingAnimation: true,
                                ),
                              ),
                            ),
                          )) : ListView.builder(
                            controller: _scrollController,
                            itemCount: _messages.length,
                            itemBuilder: (BuildContext context, int index) {
                              String? sender = _messages[index]['sender'];
                              String? message = _messages[index]['message'];

                              return FadeInUp(
                                  child: Dismissible(
                                key: Key(index.toString()), // 唯一标识符
                                direction: DismissDirection.endToStart, // 只允许从右向左滑动删除
                                onDismissed: (direction) {
                                  setState(() {
                                    _messages.removeAt(index);
                                    Global.messages_pure = _messages.map((e) => e['message']).join('
');
                                  });
                                },
                                background: Container(
                                  color: Colors.red,
                                  child: Icon(Icons.delete, color: Colors.white),
                                  alignment: Alignment.centerRight,
                                  padding: EdgeInsets.only(right: 16.0),
                                ),
                                child: Padding(
                                  padding: EdgeInsets.symmetric(vertical: 4.0),
                                  child: GestureDetector(
                                    onDoubleTap: () {
                                      Clipboard.setData(ClipboardData(text: message));
                                      ScaffoldMessenger.of(context).showSnackBar(
                                        SnackBar(
                                          content: Text('已复制到剪贴板'),
                                          duration: Duration(seconds: 1),
                                        ),
                                      );
                                    },
                                    child: Row(
                                      mainAxisAlignment: sender == '我' ? MainAxisAlignment.end : MainAxisAlignment.start,
                                      crossAxisAlignment: CrossAxisAlignment.start,
                                      children: [
                                        if (sender != '我') SizedBox(width: 8.0),
                                        Flexible(
                                          child: Container(
                                            padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
                                            decoration: BoxDecoration(
                                              color: sender == '我' ? Global.home_currentcolor : Theme.of(context).brightness == Brightness.light ? Colors.grey[200] : Colors.grey[800],
                                              borderRadius: BorderRadius.circular(20.0),
                                            ),
                                            child: MarkdownBody(
                                              data: message!,
                                            ),
                                          ),
                                        ),
                                        if (sender == '我') SizedBox(width: 8.0),
                                      ],
                                    ),
                                  ),
                                ),
                              ));
                            },
                          ),
                  ),
                ),
                Container(
                  padding: EdgeInsets.all(8.0),
                  child: Row(
                    children: [
                      IconButton(
                        icon: Icon(Icons.delete_sweep_outlined),
                        color: Global.home_currentcolor,
                        onPressed: () {
                          setState(() {
                            _showAnimation = true;
                            _messages.clear();
                            Global.messages_pure = '';
                          });
                        },
                      ),
                      Expanded(
                        child: Container(
                          padding: EdgeInsets.symmetric(horizontal: 16.0),
                          decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(20.0),
                          ),
                          child: TextField(
                            focusNode: _focusNode,
                            controller: _controller,
                            onChanged: (value) {
                              final lines = value.split('
').length;
                              setState(() {
                                _maxLines = lines + 1;
                              });
                            },
                            maxLines: _maxLines,
                            // decoration: InputDecoration.collapsed(
                            //   hintText: '输入消息...',
                            // ),
                          ),
                        ),
                      ),
                      SizedBox(width: 8.0),
                      AnimatedBuilder(
                        // 包裹IconButton的AnimatedBuilder,用于构建进度指示器
                        animation: controller,
                        builder: (BuildContext context, Widget? child) {
                          return IconButton(
                            icon: _sending ? SizedBox(
                                width: 24.0,
                                height: 24.0,
                                child: CircularProgressIndicator(
                                  strokeWidth: 2.0,
                                  valueColor: AlwaysStoppedAnimation<Color>(Global.home_currentcolor),
                                ),
                              ) : Icon(Icons.send),
                            color: Global.home_currentcolor,
                            onPressed: () async {
                              sendmessage();
                            },
                          );
                        },
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ));
  }

代码说明

  1. ListView.builder 中的 itemBuilder 中,用 Dismissible 组件包裹每个消息的 Padding 组件。
  2. 设置 Dismissiblekey 属性为 Key(index.toString()),确保每个 Dismissible 组件都有唯一的标识符。
  3. 设置 Dismissibledirection 属性为 DismissDirection.endToStart,只允许从右向左滑动删除。
  4. 设置 DismissibleonDismissed 属性为一个回调函数,该函数在消息被删除时执行。在回调函数中,使用 setState 更新消息列表 _messages,并更新全局变量 Global.messages_pure,以便其他页面可以获取最新的消息列表。
  5. 设置 Dismissiblebackground 属性为一个 Container 组件,用来显示删除图标。

完整代码

import 'dart:async';
import 'dart:convert';

import 'package:animate_do/animate_do.dart';
import 'package:animated_text_kit/animated_text_kit.dart';
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:muse_nepu_course/global.dart';
import 'package:muse_nepu_course/home.dart';
import 'package:muse_nepu_course/service/api_service.dart';
import 'package:muse_nepu_course/theme/color_schemes.g.dart';

class ChatPage extends StatefulWidget {
  @override
  _ChatPageState createState() => _ChatPageState();
}

var _filteredStringList = [];
var _stringList = [];
int _selectedIndex = -1;
String? _selectedValue;
//从assets/prompts.json中读取数据,并转换为List<Map<String, String>>
void loadprompts() async {
  String prompt = await rootBundle.loadString('assets/prompts.json');
  _stringList = json.decode(prompt);
  _filteredStringList = List.from(_stringList);
}

class _ChatPageState extends State<ChatPage>
    with TickerProviderStateMixin, WidgetsBindingObserver {
  final List<Map<String, String>> _messages = [];
  final TextEditingController _controller = TextEditingController();
  final ScrollController _scrollController = ScrollController();
  final FocusNode _focusNode = FocusNode();

  String xdata = '';
  late AnimationController controller;

  bool _sending = false;
  bool _showAnimation = true;
  bool _isAtBottom = true;

  int _maxLines = 1; // 最多显示的行数

  @override
  void initState() {
    super.initState();
    Global.bottombarheight = 60;
    _messages.clear();
    Global.messages_pure = '';
    ApiService().active_chatgpt();
    controller = AnimationController(
      duration: const Duration(milliseconds: 500),
      vsync: this,
    );
    _focusNode.requestFocus();

    loadprompts(); // 加载提示语句

    WidgetsBinding.instance!.addObserver(this);
  }

  @override
  void didChangeMetrics() {
    super.didChangeMetrics();
    if (_isAtBottom) {
      WidgetsBinding.instance!.addPostFrameCallback((_) {
        _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        theme: ThemeData(useMaterial3: true, colorScheme: lightColorScheme),
        darkTheme: ThemeData(useMaterial3: true, colorScheme: darkColorScheme),
        home: Scaffold(
          appBar: AppBar(
            backgroundColor: Global.home_currentcolor,
            title: Text(
              '双击消息复制',
              style: TextStyle(color: Colors.white),
            ),
            leading: IconButton(
              icon: Icon(Icons.arrow_back),
              onPressed: () {
                // Navigator.push(
                //     context, MaterialPageRoute(builder: (context) => HomePage()));
                //回到上一级
                Navigator.pop(context);
              },
            ),
            actions: [
              IconButton(
                icon: Icon(Icons.settings),
                onPressed: () {
                  showDialog(
                      context: context,
                      builder: (context) {
                        return Scaffold(
                            appBar: AppBar(
                              backgroundColor: Global.home_currentcolor,
                              title: Text('自动注入消息'),
                            ),
                            body: ListView.builder(
                              itemCount: _filteredStringList.length,
                              itemBuilder: (BuildContext context, int index) {
                                String act = _filteredStringList[index]['act']!;
                                String prompt =
                                    _filteredStringList[index]['prompt']!;
                                return RadioListTile(
                                  title: Text('行为: $act'),
                                  subtitle: Text('语句: $prompt'),
                                  value: index,
                                  groupValue: _selectedIndex,
                                  onChanged: (int? value) {
                                    setState(() {
                                      _selectedIndex = value!;
                                      _selectedValue =
                                          _filteredStringList[_selectedIndex]['act'];
                                      //发送消息

                                      _controller.text =
                                          _filteredStringList[_selectedIndex]['prompt'];
                                      //返回主页面
                                      Navigator.pop(context);
                                    });
                                  },
                                );
                              },
                            ));
                      });
                },
              ),
            ],
          ),
          body: Container(
            child: Column(
              children: [
                Expanded(
                  child: Container(
                    padding: EdgeInsets.all(12.0),
                    child: _messages.isEmpty
                        ? FadeInUp(
                            child: AnimatedOpacity(
                            opacity: _showAnimation ? 1.0 : 0.0,
                            duration: Duration(milliseconds: 500),
                            child: Center(
                              child: DefaultTextStyle(
                                style: TextStyle(
                                  fontSize: 24.0,
                                  fontWeight: FontWeight.bold,
                                ),
                                child: AnimatedTextKit(
                                  animatedTexts: [
                                    ColorizeAnimatedText(
                                      'chatgpt',
                                      colors: [
                                        Colors.purple,
                                        Colors.blue,
                                        Colors.yellow,
                                        Colors.red,
                                      ],
                                      textStyle: TextStyle(fontSize: 50.0),
                                    ),
                                  ],
                                  isRepeatingAnimation: true,
                                ),
                              ),
                            ),
                          )) : ListView.builder(
                            controller: _scrollController,
                            itemCount: _messages.length,
                            itemBuilder: (BuildContext context, int index) {
                              String? sender = _messages[index]['sender'];
                              String? message = _messages[index]['message'];

                              return FadeInUp(
                                  child: Dismissible(
                                key: Key(index.toString()), // 唯一标识符
                                direction: DismissDirection.endToStart, // 只允许从右向左滑动删除
                                onDismissed: (direction) {
                                  setState(() {
                                    _messages.removeAt(index);
                                    Global.messages_pure = _messages.map((e) => e['message']).join('
');
                                  });
                                },
                                background: Container(
                                  color: Colors.red,
                                  child: Icon(Icons.delete, color: Colors.white),
                                  alignment: Alignment.centerRight,
                                  padding: EdgeInsets.only(right: 16.0),
                                ),
                                child: Padding(
                                  padding: EdgeInsets.symmetric(vertical: 4.0),
                                  child: GestureDetector(
                                    onDoubleTap: () {
                                      Clipboard.setData(ClipboardData(text: message));
                                      ScaffoldMessenger.of(context).showSnackBar(
                                        SnackBar(
                                          content: Text('已复制到剪贴板'),
                                          duration: Duration(seconds: 1),
                                        ),
                                      );
                                    },
                                    child: Row(
                                      mainAxisAlignment: sender == '我' ? MainAxisAlignment.end : MainAxisAlignment.start,
                                      crossAxisAlignment: CrossAxisAlignment.start,
                                      children: [
                                        if (sender != '我') SizedBox(width: 8.0),
                                        Flexible(
                                          child: Container(
                                            padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
                                            decoration: BoxDecoration(
                                              color: sender == '我' ? Global.home_currentcolor : Theme.of(context).brightness == Brightness.light ? Colors.grey[200] : Colors.grey[800],
                                              borderRadius: BorderRadius.circular(20.0),
                                            ),
                                            child: MarkdownBody(
                                              data: message!,
                                            ),
                                          ),
                                        ),
                                        if (sender == '我') SizedBox(width: 8.0),
                                      ],
                                    ),
                                  ),
                                ),
                              ));
                            },
                          ),
                  ),
                ),
                Container(
                  padding: EdgeInsets.all(8.0),
                  child: Row(
                    children: [
                      IconButton(
                        icon: Icon(Icons.delete_sweep_outlined),
                        color: Global.home_currentcolor,
                        onPressed: () {
                          setState(() {
                            _showAnimation = true;
                            _messages.clear();
                            Global.messages_pure = '';
                          });
                        },
                      ),
                      Expanded(
                        child: Container(
                          padding: EdgeInsets.symmetric(horizontal: 16.0),
                          decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(20.0),
                          ),
                          child: TextField(
                            focusNode: _focusNode,
                            controller: _controller,
                            onChanged: (value) {
                              final lines = value.split('
').length;
                              setState(() {
                                _maxLines = lines + 1;
                              });
                            },
                            maxLines: _maxLines,
                            // decoration: InputDecoration.collapsed(
                            //   hintText: '输入消息...',
                            // ),
                          ),
                        ),
                      ),
                      SizedBox(width: 8.0),
                      AnimatedBuilder(
                        // 包裹IconButton的AnimatedBuilder,用于构建进度指示器
                        animation: controller,
                        builder: (BuildContext context, Widget? child) {
                          return IconButton(
                            icon: _sending ? SizedBox(
                                width: 24.0,
                                height: 24.0,
                                child: CircularProgressIndicator(
                                  strokeWidth: 2.0,
                                  valueColor: AlwaysStoppedAnimation<Color>(Global.home_currentcolor),
                                ),
                              ) : Icon(Icons.send),
                            color: Global.home_currentcolor,
                            onPressed: () async {
                              sendmessage();
                            },
                          );
                        },
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ));
  }

效果展示

现在,每条消息都可以通过向左滑动来删除了。

[图片展示]

总结

本文介绍了如何在 Flutter Chat App 中为每条消息添加一个删除按钮,实现滑动删除功能。使用 Dismissible 组件可以方便地实现这个功能,代码简洁易懂。

希望这篇文章对您有所帮助!

Flutter Chat App: 添加消息删除功能

原文地址: https://www.cveoy.top/t/topic/ovqM 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录