flyerhq / flutter_chat_ui

Actively maintained, community-driven chat UI implementation with an optional Firebase BaaS.
https://flyer.chat
Apache License 2.0
1.59k stars 689 forks source link

error _debugcurrentbuildtarget == context' is not true when message content is larger than screen size #417

Closed Nha58131355 closed 1 year ago

Nha58131355 commented 1 year ago

General

What bug do you experience? 🐞

"'package:flutter/src/widgets/framework.dart': Failed assertion: line 2685 pos 20: '_debugCurrentBuildTarget == context': is not true." when message content is larger than screen size

How can it be reproduced? πŸ€”

A few steps to define where does the bug occur. Step 1. add messages until the existing screen size is exceeded

What behavior is expected? πŸ’‘

I wish no more errors


Extras

Screenshots or videos πŸ“Έ

image

https://user-images.githubusercontent.com/46096405/229013685-3bcc4cd7-b8cd-42b8-a223-13009335c656.mp4

https://user-images.githubusercontent.com/46096405/229013853-02ac6485-c650-4d95-b24b-c717f408ccbf.mp4

Code snippets πŸ“

If applicable, add code samples to help explain your problem.

import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_chat_ui/flutter_chat_ui.dart';
// ignore: depend_on_referenced_packages
import 'package:flutter_chat_types/flutter_chat_types.dart' as types;
import 'package:flutter_svg/svg.dart';
import 'package:image_picker/image_picker.dart';
import 'package:mime/mime.dart';
import 'package:open_filex/open_filex.dart';
import 'package:path_provider/path_provider.dart';
import 'package:u2u_exchange/config/global_translations.dart';
import 'package:u2u_exchange/config/theme/dimens.dart';
import 'package:u2u_exchange/config/theme/text.dart';
import 'package:u2u_exchange/utils/app_asset.dart';
import 'package:u2u_exchange/utils/app_color.dart';
import 'package:file_picker/file_picker.dart';
// ignore: depend_on_referenced_packages
import 'package:http/http.dart' as http;

class ChatSrceen extends StatefulWidget {
  const ChatSrceen({super.key});

  @override
  State<ChatSrceen> createState() => _ChatSrceenState();
}

class _ChatSrceenState extends State<ChatSrceen> {
  List<types.Message> _messages = [
    types.TextMessage(
      author: const types.User(id: 'id-1'),
      createdAt: DateTime.now().millisecondsSinceEpoch,
      id: "id",
      text: 'Giao dα»‹ch P2P',
    ),
  ];
  final _user = const types.User(id: 'id-2');

  bool _isAttachmentUploading = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: IconButton(
          icon: const Icon(
            Icons.arrow_back_ios_rounded,
            color: AppColors.white,
          ),
          onPressed: () => Navigator.of(context).pop(),
        ),
        title: Row(
          children: [
            Image.asset('assets/images/avata_u2u.png'),
            SizedBox(
              width: size_8_w,
            ),
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  'Nguyen Ba Nha',
                  style: Theme.of(context).textTheme.displayLarge!.copyWith(fontSize: text_14),
                ),
                SizedBox(
                  height: size_8_h,
                ),
                Text(
                  '09235678943',
                  style: Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: text_14),
                ),
              ],
            ),
          ],
        ),
        centerTitle: false,
      ),
      body: _buildChat(),
    );
  }

  Widget _buildChat() {
    return Chat(
      messages: _messages,
      onAttachmentPressed: _handleAttachmentPressed,
      onMessageTap: _handleMessageTap,
      onPreviewDataFetched: _handlePreviewDataFetched,
      onSendPressed: _handleSendPressed,
      showUserAvatars: true,
      showUserNames: true,
      user: _user,
      emptyState: Center(
          child: Text(
        u2uText.text('noMessages'),
        style: Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: text_14),
      )),
      //onAttachmentPressed: _handleAtachmentPressed,
      theme: DefaultChatTheme(
        backgroundColor: AppColors.backgroudColor,
        messageBorderRadius: size_radius_8,
        primaryColor: AppColors.green,
        secondaryColor: AppColors.backgroundBottombar,
        sentMessageBodyTextStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(fontSize: text_16, fontWeight: FontWeight.w400, color: AppColors.black),
        receivedMessageBodyTextStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(fontSize: text_16, fontWeight: FontWeight.w400, color: AppColors.white),
        inputBackgroundColor: AppColors.backgroudColor,
        inputBorderRadius: BorderRadius.circular(0),
        inputPadding: EdgeInsets.all(size_16_w),
        sendButtonIcon: SvgPicture.asset(AppAssets.icon_send),
        inputTextDecoration: InputDecoration(
          hintText: 'NhαΊ­p nα»™i dung...',
          hintStyle: TextStyle(color: AppColors.hintText, fontWeight: FontWeight.normal, fontSize: text_14),
          contentPadding: EdgeInsets.symmetric(
            vertical: size_8_h,
            horizontal: size_16_h,
          ),
          focusedBorder: OutlineInputBorder(
            borderRadius: BorderRadius.all(Radius.circular(size_radius_8)),
            borderSide: const BorderSide(color: AppColors.green),
          ),
          enabledBorder: OutlineInputBorder(
            borderRadius: BorderRadius.all(Radius.circular(size_radius_8)),
            borderSide: const BorderSide(color: AppColors.colorBorder),
          ),
        ),
      ),
    );
  }

  void _addMessage(types.Message message) {
    setState(() {
      _messages.insert(0, message);
    });
  }

  void _handleAttachmentPressed() {
    showModalBottomSheet<void>(
      context: context,
      builder: (BuildContext context) => SafeArea(
        child: SizedBox(
          height: 144,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              TextButton(
                onPressed: () {
                  Navigator.pop(context);
                  _handleImageSelection();
                },
                child: const Align(
                  alignment: AlignmentDirectional.centerStart,
                  child: Text('Photo'),
                ),
              ),
              TextButton(
                onPressed: () {
                  Navigator.pop(context);
                  _handleFileSelection();
                },
                child: const Align(
                  alignment: AlignmentDirectional.centerStart,
                  child: Text('File'),
                ),
              ),
              TextButton(
                onPressed: () => Navigator.pop(context),
                child: const Align(
                  alignment: AlignmentDirectional.centerStart,
                  child: Text('Cancel'),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  void _handleFileSelection() async {
    final result = await FilePicker.platform.pickFiles(
      type: FileType.any,
    );

    if (result != null && result.files.single.path != null) {
      final message = types.FileMessage(
        author: _user,
        createdAt: DateTime.now().millisecondsSinceEpoch,
        id: 'id',
        mimeType: lookupMimeType(result.files.single.path!),
        name: result.files.single.name,
        size: result.files.single.size,
        uri: result.files.single.path!,
      );

      _addMessage(message);
    }
  }

  void _handleImageSelection() async {
    final result = await ImagePicker().pickImage(
      imageQuality: 70,
      maxWidth: 1440,
      source: ImageSource.gallery,
    );

    if (result != null) {
      final bytes = await result.readAsBytes();
      final image = await decodeImageFromList(bytes);

      final message = types.ImageMessage(
        author: _user,
        createdAt: DateTime.now().millisecondsSinceEpoch,
        height: image.height.toDouble(),
        id: 'id',
        name: result.name,
        size: bytes.length,
        uri: result.path,
        width: image.width.toDouble(),
      );

      _addMessage(message);
    }
  }

  void _handleMessageTap(BuildContext _, types.Message message) async {
    if (message is types.FileMessage) {
      var localPath = message.uri;

      if (message.uri.startsWith('http')) {
        try {
          final index = _messages.indexWhere((element) => element.id == message.id);
          final updatedMessage = (_messages[index] as types.FileMessage).copyWith(
            isLoading: true,
          );

          setState(() {
            _messages[index] = updatedMessage;
          });

          final client = http.Client();
          final request = await client.get(Uri.parse(message.uri));
          final bytes = request.bodyBytes;
          final documentsDir = (await getApplicationDocumentsDirectory()).path;
          localPath = '$documentsDir/${message.name}';

          if (!File(localPath).existsSync()) {
            final file = File(localPath);
            await file.writeAsBytes(bytes);
          }
        } finally {
          final index = _messages.indexWhere((element) => element.id == message.id);
          final updatedMessage = (_messages[index] as types.FileMessage).copyWith(
            isLoading: null,
          );

          setState(() {
            _messages[index] = updatedMessage;
          });
        }
      }

      await OpenFilex.open(localPath);
    }
  }

  void _handlePreviewDataFetched(
    types.TextMessage message,
    types.PreviewData previewData,
  ) {
    final index = _messages.indexWhere((element) => element.id == message.id);
    final updatedMessage = (_messages[index] as types.TextMessage).copyWith(
      previewData: previewData,
    );

    setState(() {
      _messages[index] = updatedMessage;
    });
  }

  void _handleSendPressed(types.PartialText message) {
    final textMessage = types.TextMessage(
      author: _user,
      createdAt: DateTime.now().millisecondsSinceEpoch,
      id: 'id',
      text: message.text,
    );

    _addMessage(textMessage);
  }

  void _loadMessages() async {
    final response = await rootBundle.loadString('assets/messages.json');
    final messages = (jsonDecode(response) as List).map((e) => types.Message.fromJson(e as Map<String, dynamic>)).toList();

    setState(() {
      _messages = messages;
    });
  }
}


Environment info

Please specify the flutter, flutter-chat-ui versions.

flutter: 3.7.9 flutter-chat-ui: ^1.6.6

flutter doctor -v output πŸ‘‡

[√] Flutter (Channel stable, 3.7.8, on Microsoft Windows [Version 10.0.19041.1415], locale en-US) [√] Windows Version (Installed version of Windows is version 10 or higher) [√] Android toolchain - develop for Android devices (Android SDK version 33.0.0) [√] Chrome - develop for the web [X] Visual Studio - develop for Windows X Visual Studio not installed; this is necessary for Windows development. Download at https://visualstudio.microsoft.com/downloads/. Please install the "Desktop development with C++" workload, including all of its default components [√] Android Studio (version 2021.3) [√] VS Code (version 1.76.2) [√] Connected device (4 available) [√] HTTP Host Availability

! Doctor found issues in 1 category.

Platform

Device: Android emulator Galaxy S7

OS version: Android 10


mrpurpleknight commented 1 year ago

General

What bug do you experience? 🐞

"'package:flutter/src/widgets/framework.dart': Failed assertion: line 2685 pos 20: '_debugCurrentBuildTarget == context': is not true." when message content is larger than screen size

How can it be reproduced? πŸ€”

A few steps to define where does the bug occur. Step 1. add messages until the existing screen size is exceeded

What behavior is expected? πŸ’‘

I wish no more errors

Extras

Screenshots or videos πŸ“Έ

image

bandicam.2023-03-31.10-12-15-849.mp4 bandicam.2023-03-31.10-14-28-584.mp4

Code snippets πŸ“

If applicable, add code samples to help explain your problem.

import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_chat_ui/flutter_chat_ui.dart';
// ignore: depend_on_referenced_packages
import 'package:flutter_chat_types/flutter_chat_types.dart' as types;
import 'package:flutter_svg/svg.dart';
import 'package:image_picker/image_picker.dart';
import 'package:mime/mime.dart';
import 'package:open_filex/open_filex.dart';
import 'package:path_provider/path_provider.dart';
import 'package:u2u_exchange/config/global_translations.dart';
import 'package:u2u_exchange/config/theme/dimens.dart';
import 'package:u2u_exchange/config/theme/text.dart';
import 'package:u2u_exchange/utils/app_asset.dart';
import 'package:u2u_exchange/utils/app_color.dart';
import 'package:file_picker/file_picker.dart';
// ignore: depend_on_referenced_packages
import 'package:http/http.dart' as http;

class ChatSrceen extends StatefulWidget {
  const ChatSrceen({super.key});

  @override
  State<ChatSrceen> createState() => _ChatSrceenState();
}

class _ChatSrceenState extends State<ChatSrceen> {
  List<types.Message> _messages = [
    types.TextMessage(
      author: const types.User(id: 'id-1'),
      createdAt: DateTime.now().millisecondsSinceEpoch,
      id: "id",
      text: 'Giao dα»‹ch P2P',
    ),
  ];
  final _user = const types.User(id: 'id-2');

  bool _isAttachmentUploading = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: IconButton(
          icon: const Icon(
            Icons.arrow_back_ios_rounded,
            color: AppColors.white,
          ),
          onPressed: () => Navigator.of(context).pop(),
        ),
        title: Row(
          children: [
            Image.asset('assets/images/avata_u2u.png'),
            SizedBox(
              width: size_8_w,
            ),
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  'Nguyen Ba Nha',
                  style: Theme.of(context).textTheme.displayLarge!.copyWith(fontSize: text_14),
                ),
                SizedBox(
                  height: size_8_h,
                ),
                Text(
                  '09235678943',
                  style: Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: text_14),
                ),
              ],
            ),
          ],
        ),
        centerTitle: false,
      ),
      body: _buildChat(),
    );
  }

  Widget _buildChat() {
    return Chat(
      messages: _messages,
      onAttachmentPressed: _handleAttachmentPressed,
      onMessageTap: _handleMessageTap,
      onPreviewDataFetched: _handlePreviewDataFetched,
      onSendPressed: _handleSendPressed,
      showUserAvatars: true,
      showUserNames: true,
      user: _user,
      emptyState: Center(
          child: Text(
        u2uText.text('noMessages'),
        style: Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: text_14),
      )),
      //onAttachmentPressed: _handleAtachmentPressed,
      theme: DefaultChatTheme(
        backgroundColor: AppColors.backgroudColor,
        messageBorderRadius: size_radius_8,
        primaryColor: AppColors.green,
        secondaryColor: AppColors.backgroundBottombar,
        sentMessageBodyTextStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(fontSize: text_16, fontWeight: FontWeight.w400, color: AppColors.black),
        receivedMessageBodyTextStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(fontSize: text_16, fontWeight: FontWeight.w400, color: AppColors.white),
        inputBackgroundColor: AppColors.backgroudColor,
        inputBorderRadius: BorderRadius.circular(0),
        inputPadding: EdgeInsets.all(size_16_w),
        sendButtonIcon: SvgPicture.asset(AppAssets.icon_send),
        inputTextDecoration: InputDecoration(
          hintText: 'NhαΊ­p nα»™i dung...',
          hintStyle: TextStyle(color: AppColors.hintText, fontWeight: FontWeight.normal, fontSize: text_14),
          contentPadding: EdgeInsets.symmetric(
            vertical: size_8_h,
            horizontal: size_16_h,
          ),
          focusedBorder: OutlineInputBorder(
            borderRadius: BorderRadius.all(Radius.circular(size_radius_8)),
            borderSide: const BorderSide(color: AppColors.green),
          ),
          enabledBorder: OutlineInputBorder(
            borderRadius: BorderRadius.all(Radius.circular(size_radius_8)),
            borderSide: const BorderSide(color: AppColors.colorBorder),
          ),
        ),
      ),
    );
  }

  void _addMessage(types.Message message) {
    setState(() {
      _messages.insert(0, message);
    });
  }

  void _handleAttachmentPressed() {
    showModalBottomSheet<void>(
      context: context,
      builder: (BuildContext context) => SafeArea(
        child: SizedBox(
          height: 144,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              TextButton(
                onPressed: () {
                  Navigator.pop(context);
                  _handleImageSelection();
                },
                child: const Align(
                  alignment: AlignmentDirectional.centerStart,
                  child: Text('Photo'),
                ),
              ),
              TextButton(
                onPressed: () {
                  Navigator.pop(context);
                  _handleFileSelection();
                },
                child: const Align(
                  alignment: AlignmentDirectional.centerStart,
                  child: Text('File'),
                ),
              ),
              TextButton(
                onPressed: () => Navigator.pop(context),
                child: const Align(
                  alignment: AlignmentDirectional.centerStart,
                  child: Text('Cancel'),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  void _handleFileSelection() async {
    final result = await FilePicker.platform.pickFiles(
      type: FileType.any,
    );

    if (result != null && result.files.single.path != null) {
      final message = types.FileMessage(
        author: _user,
        createdAt: DateTime.now().millisecondsSinceEpoch,
        id: 'id',
        mimeType: lookupMimeType(result.files.single.path!),
        name: result.files.single.name,
        size: result.files.single.size,
        uri: result.files.single.path!,
      );

      _addMessage(message);
    }
  }

  void _handleImageSelection() async {
    final result = await ImagePicker().pickImage(
      imageQuality: 70,
      maxWidth: 1440,
      source: ImageSource.gallery,
    );

    if (result != null) {
      final bytes = await result.readAsBytes();
      final image = await decodeImageFromList(bytes);

      final message = types.ImageMessage(
        author: _user,
        createdAt: DateTime.now().millisecondsSinceEpoch,
        height: image.height.toDouble(),
        id: 'id',
        name: result.name,
        size: bytes.length,
        uri: result.path,
        width: image.width.toDouble(),
      );

      _addMessage(message);
    }
  }

  void _handleMessageTap(BuildContext _, types.Message message) async {
    if (message is types.FileMessage) {
      var localPath = message.uri;

      if (message.uri.startsWith('http')) {
        try {
          final index = _messages.indexWhere((element) => element.id == message.id);
          final updatedMessage = (_messages[index] as types.FileMessage).copyWith(
            isLoading: true,
          );

          setState(() {
            _messages[index] = updatedMessage;
          });

          final client = http.Client();
          final request = await client.get(Uri.parse(message.uri));
          final bytes = request.bodyBytes;
          final documentsDir = (await getApplicationDocumentsDirectory()).path;
          localPath = '$documentsDir/${message.name}';

          if (!File(localPath).existsSync()) {
            final file = File(localPath);
            await file.writeAsBytes(bytes);
          }
        } finally {
          final index = _messages.indexWhere((element) => element.id == message.id);
          final updatedMessage = (_messages[index] as types.FileMessage).copyWith(
            isLoading: null,
          );

          setState(() {
            _messages[index] = updatedMessage;
          });
        }
      }

      await OpenFilex.open(localPath);
    }
  }

  void _handlePreviewDataFetched(
    types.TextMessage message,
    types.PreviewData previewData,
  ) {
    final index = _messages.indexWhere((element) => element.id == message.id);
    final updatedMessage = (_messages[index] as types.TextMessage).copyWith(
      previewData: previewData,
    );

    setState(() {
      _messages[index] = updatedMessage;
    });
  }

  void _handleSendPressed(types.PartialText message) {
    final textMessage = types.TextMessage(
      author: _user,
      createdAt: DateTime.now().millisecondsSinceEpoch,
      id: 'id',
      text: message.text,
    );

    _addMessage(textMessage);
  }

  void _loadMessages() async {
    final response = await rootBundle.loadString('assets/messages.json');
    final messages = (jsonDecode(response) as List).map((e) => types.Message.fromJson(e as Map<String, dynamic>)).toList();

    setState(() {
      _messages = messages;
    });
  }
}

Environment info

Please specify the flutter, flutter-chat-ui versions.

flutter: 3.7.9 flutter-chat-ui: ^1.6.6

flutter doctor -v output πŸ‘‡

[√] Flutter (Channel stable, 3.7.8, on Microsoft Windows [Version 10.0.19041.1415], locale en-US) [√] Windows Version (Installed version of Windows is version 10 or higher) [√] Android toolchain - develop for Android devices (Android SDK version 33.0.0) [√] Chrome - develop for the web [X] Visual Studio - develop for Windows X Visual Studio not installed; this is necessary for Windows development. Download at https://visualstudio.microsoft.com/downloads/. Please install the "Desktop development with C++" workload, including all of its default components [√] Android Studio (version 2021.3) [√] VS Code (version 1.76.2) [√] Connected device (4 available) [√] HTTP Host Availability

! Doctor found issues in 1 category.

Platform

Device: Android emulator Galaxy S7

OS version: Android 10

The problem is that you are adding messages with the same id to the Chat widget

demchenkoalex commented 1 year ago

message ids should be unique. let me know if this does not fix the problem.