GetStream / stream-chat-flutter

Flutter Chat SDK - Build your own chat app experience using Dart, Flutter and the Stream Chat Messaging API.
https://getstream.io/chat/sdk/flutter/
Other
880 stars 313 forks source link

always showing 'You must have a Stream Chat Theme widget at the top of your widget tree' #1952

Closed adityasethipsi closed 1 week ago

adityasethipsi commented 2 weeks ago

Which packages are you using?

stream_chat_flutter

On what platforms did you experience the issue?

Web

What version are you using?

7.2.1

What happened?

When I long press on any message, then it shows this error as shown in SS.

image

Steps to reproduce

long press on any message

Supporting info to reproduce

// ignore_for_file: prefer-single-widget-per-file

import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';

class StreamChatPage extends StatefulWidget {
  final StreamChatClient client;
  const StreamChatPage({
    super.key,
    required this.client,
  });

  @override
  StreamChatPageState createState() => StreamChatPageState();
}

class StreamChatPageState extends State<StreamChatPage> {
  late Channel channel;

  StreamChatPageState();

  @override
  void dispose() {
    channel.stopWatching();
    channel.dispose();
    super.dispose();
  }

  @override
  void initState() {
    super.initState();
    channel = widget.client.channel(
      'messaging',
      id: 'flutterDevs',
    );

    // _init();
  }

  // Future<void> _init() async {
  //   // var authToken = await appController.getAuthToken();
  //   // client = StreamChatClient('z8rtr6acsden', logLevel: Level.INFO);
  //   // await client.connectUser(
  //   //   User(id: 'abc'),
  //   //   'Bearer ${authToken ?? 'ANNMCNS'}',
  //   // );
  // }

  @override
  Widget build(BuildContext context) {
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      channel.watch();
    });
    final theme = ThemeData(
      primarySwatch: Colors.green,
    );
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData.light(),
      darkTheme: ThemeData.dark(),
      // themeMode: ThemeMode.dark,
      supportedLocales: const [
        Locale('en'),
        Locale('hi'),
        Locale('fr'),
        Locale('it'),
        Locale('es'),
      ],
      builder: (context, a) => StreamChat(
        client: widget.client,
        child: a ?? Container(),
      ),
      home: StreamChannel(
        channel: channel,
        child: ChannelPage(channel),
      ),
    );
  }
}

class ChannelPage extends StatefulWidget {
  final Channel channel;

  const ChannelPage(this.channel);

  @override
  _ChannelPageState createState() => _ChannelPageState();
}

class _ChannelPageState extends State<ChannelPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Expanded(
            child: StreamMessageListView(
              messageBuilder: (p0, p1, p2, defaultMessageWidget) {
                return defaultMessageWidget.copyWith(
                  showDeleteMessage: true,
                  showEditMessage: true,
                  showReactionPicker: true,
                  showReactions: true,
                  showReplyMessage: true,
                );
                StreamMessageWidget;
              },

              // onMessageTap: (message) {
              //   showModalBottomSheet(
              //     context: context,
              //     builder: (context) {
              //       return ReactionPicker(
              //         message: message,
              //         onReactionTap: (reactionType) async {
              //           await widget.channel.sendReaction(
              //             message,
              //             reactionType,
              //           );
              //           Navigator.of(context).pop();
              //         },
              //       );
              //     },
              //   );
              // },
              // messageBuilder: (p0, p1, p2, defaultMessageWidget) {
              //   if (p1.message.isDeleted) return SizedBox();
              //   return InkWell(
              //     // onDoubleTap: () async {
              //     //   var status = await channel.unbanMember(
              //     //     'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoicDJZMFVvenI0N09ETFVEdmcyQmh0cnRNV1dqMiJ9.ky0c5iEZIMAh72PwxWKBj3Efr2pjgvgM3507m7qol44',
              //     //     //  BanUserRequest(
              //     //     //   timeout: 60, // Ban duration in minutes, omit or set to 0 for permanent ban
              //     //     //   reason: 'Violation of community guidelines',
              //     //     // ),
              //     //   );
              //     //   status;
              //     // },
              //     // onLongPress: () async {
              //     //   var status = await channel.banMember(
              //     //     'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoicDJZMFVvenI0N09ETFVEdmcyQmh0cnRNV1dqMiJ9.ky0c5iEZIMAh72PwxWKBj3Efr2pjgvgM3507m7qol44',
              //     //     {
              //     //       'ttl': 18,
              //     //     },
              //     //     //  BanUserRequest(
              //     //     //   timeout: 60, // Ban duration in minutes, omit or set to 0 for permanent ban
              //     //     //   reason: 'Violation of community guidelines',
              //     //     // ),
              //     //   );
              //     //   status;
              //     // },
              //     // onTap: () {
              //     //   showModalBottomSheet(
              //     //     context: context,
              //     //     builder: (context) {
              //     //       return ReactionPicker(
              //     //         message: p1.message,
              //     //         onReactionTap: (reactionType) async {
              //     //           await channel.sendReaction(
              //     //             p1.message,
              //     //             reactionType,
              //     //           );
              //     //           Navigator.of(context).pop();
              //     //         },
              //     //       );
              //     //     },
              //     //   );
              //     // },
              //     child: Row(
              //       children: [
              //         Text(p1.message.text ?? 'am,s'),
              //         IconButton(
              //           icon: Icon(Icons.delete),
              //           onPressed: () {
              //             widget.channel.deleteMessage(p1.message, hard: true);
              //             setState(() {});
              //           },
              //         ),
              //         IconButton(
              //           icon: Icon(Icons.edit),
              //           onPressed: () async {
              //             await Get.dialog(SetSupplierNameDialog(
              //                 'Edit your company name ',
              //                 p1.message.text!,
              //                 'You can change your company name. This will be updated on all your open and submitted bids.',
              //                 (old, newA) {
              //               var newMessage = Message(
              //                 text: p1.message.text,
              //                 user: p1.message.user,
              //               );
              //               // p1.message.text = newA;
              //               widget.channel.updateMessage(p1.message);
              //             }));
              //           },
              //         ),
              //         IconButton(
              //           icon: Icon(Icons.stop_circle),
              //           onPressed: () {
              //             widget.channel.banMember(
              //               p1.message.user!.id,
              //               {'reason': 'myChoice'},
              //             );
              //             setState(() {});
              //           },
              //         ),
              //         IconButton(
              //           icon: Icon(Icons.pause),
              //           onPressed: () {
              //             widget.channel.removeMembers([p1.message.user!.id]);
              //             setState(() {});
              //           },
              //         ),
              //       ],
              //     ),
              //   );
              // },
            ),
          ),
          const StreamMessageInput(
            allowedAttachmentPickerTypes: [AttachmentPickerType.files],
          ),
        ],
      ),
    );
  }
}

const riveStreamReactionAnimations = [
  RiveStreamReaction(
    type: 'love',
    artboard: 'love',
    artboardHighlighted: 'love_highlight',
  ),
  RiveStreamReaction(
    type: 'like',
    artboard: 'like',
    artboardHighlighted: 'like_highlight',
  ),
  RiveStreamReaction(
    type: 'sad',
    artboard: 'sad',
    artboardHighlighted: 'sad_highlight',
  ),
  RiveStreamReaction(
    type: 'haha',
    artboard: 'haha',
    artboardHighlighted: 'haha_highlight',
  ),
  RiveStreamReaction(
    type: 'wow',
    artboard: 'wow',
    artboardHighlighted: 'wow_highlight',
  ),
];

@immutable
class RiveStreamReaction {
  final String type;
  final String artboard;
  final String artboardHighlighted;

  const RiveStreamReaction({
    required this.type,
    required this.artboard,
    required this.artboardHighlighted,
  });
}

class ReactionPicker extends StatelessWidget {
  final Message message;
  final void Function(String) onReactionTap;

  const ReactionPicker({
    Key? key,
    required this.message,
    required this.onReactionTap,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Wrap(
      children: [
        IconButton(
          icon: const Text('πŸ˜€'),
          onPressed: () => onReactionTap('smile'),
        ),
        IconButton(
          icon: const Text('😒'),
          onPressed: () => onReactionTap('sad'),
        ),
        IconButton(
          icon: const Text('πŸ‘'),
          onPressed: () => onReactionTap('thumbs_up'),
        ),
        IconButton(
          icon: const Text('❀️'),
          onPressed: () => onReactionTap('heart'),
        ),
      ],
    );
  }
}

Relevant log output

No response

Flutter analyze output

No response

Flutter doctor output

No response

Code of Conduct

adityasethipsi commented 2 weeks ago

Please need support on this ASAP.

deven98 commented 2 weeks ago

Hey @adityasethipsi πŸ‘‹

This usually occurs in a situation when the StreamChat widget is inaccessible by the Stream widget using the theme. Try checking through how you're using any navigation.

As for the sample you provided, the same works for me on 7.2.1 - here is a complete sample using your code if you want to run this yourself:

(Paste this in your main.dart and hit run)

// ignore_for_file: public_member_api_docs

import 'dart:async';
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  /// Create a new instance of [StreamChatClient] passing the apikey obtained
  /// from your project dashboard.
  final client = StreamChatClient(
    's2dxdhpxd94g',
    logLevel: Level.OFF,
  );

  /// Set the current user and connect the websocket. In a production
  /// scenario, this should be done using a backend to generate a user token
  /// using our server SDK.
  ///
  /// Please see the following for more information:
  /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/
  await client.connectUser(
    User(id: 'super-band-9'),
    '''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''',
  );

  runApp(
    StreamChatPage(
      client: client,
    ),
  );
}

class StreamChatPage extends StatefulWidget {
  final StreamChatClient client;
  const StreamChatPage({
    super.key,
    required this.client,
  });

  @override
  StreamChatPageState createState() => StreamChatPageState();
}

class StreamChatPageState extends State<StreamChatPage> {
  late Channel channel;

  StreamChatPageState();

  @override
  void dispose() {
    channel.stopWatching();
    channel.dispose();
    super.dispose();
  }

  @override
  void initState() {
    super.initState();
    channel = widget.client.channel(
      'messaging',
      id: 'flutterDevs',
    );
  }

  @override
  Widget build(BuildContext context) {
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      channel.watch();
    });
    final theme = ThemeData(
      primarySwatch: Colors.green,
    );
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData.light(),
      darkTheme: ThemeData.dark(),
      // themeMode: ThemeMode.dark,
      supportedLocales: const [
        Locale('en'),
        Locale('hi'),
        Locale('fr'),
        Locale('it'),
        Locale('es'),
      ],
      builder: (context, a) => StreamChat(
        client: widget.client,
        child: a ?? Container(),
      ),
      home: StreamChannel(
        channel: channel,
        child: ChannelPage(channel),
      ),
    );
  }
}

class ChannelPage extends StatefulWidget {
  final Channel channel;

  const ChannelPage(this.channel);

  @override
  _ChannelPageState createState() => _ChannelPageState();
}

class _ChannelPageState extends State<ChannelPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Expanded(
            child: StreamMessageListView(
              messageBuilder: (p0, p1, p2, defaultMessageWidget) {
                return defaultMessageWidget.copyWith(
                  showDeleteMessage: true,
                  showEditMessage: true,
                  showReactionPicker: true,
                  showReactions: true,
                  showReplyMessage: true,
                );
                StreamMessageWidget;
              },
            ),
          ),
          const StreamMessageInput(
            allowedAttachmentPickerTypes: [AttachmentPickerType.files],
          ),
        ],
      ),
    );
  }
}

const riveStreamReactionAnimations = [
  RiveStreamReaction(
    type: 'love',
    artboard: 'love',
    artboardHighlighted: 'love_highlight',
  ),
  RiveStreamReaction(
    type: 'like',
    artboard: 'like',
    artboardHighlighted: 'like_highlight',
  ),
  RiveStreamReaction(
    type: 'sad',
    artboard: 'sad',
    artboardHighlighted: 'sad_highlight',
  ),
  RiveStreamReaction(
    type: 'haha',
    artboard: 'haha',
    artboardHighlighted: 'haha_highlight',
  ),
  RiveStreamReaction(
    type: 'wow',
    artboard: 'wow',
    artboardHighlighted: 'wow_highlight',
  ),
];

@immutable
class RiveStreamReaction {
  final String type;
  final String artboard;
  final String artboardHighlighted;

  const RiveStreamReaction({
    required this.type,
    required this.artboard,
    required this.artboardHighlighted,
  });
}

class ReactionPicker extends StatelessWidget {
  final Message message;
  final void Function(String) onReactionTap;

  const ReactionPicker({
    Key? key,
    required this.message,
    required this.onReactionTap,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Wrap(
      children: [
        IconButton(
          icon: const Text('πŸ˜€'),
          onPressed: () => onReactionTap('smile'),
        ),
        IconButton(
          icon: const Text('😒'),
          onPressed: () => onReactionTap('sad'),
        ),
        IconButton(
          icon: const Text('πŸ‘'),
          onPressed: () => onReactionTap('thumbs_up'),
        ),
        IconButton(
          icon: const Text('❀️'),
          onPressed: () => onReactionTap('heart'),
        ),
      ],
    );
  }
}

Let me know if there's something I'm missing here. If you're still getting this error after trying the sample above, please add the code you're using to connect and get until the messaging page. Thanks.

deven98 commented 1 week ago

Closing this for now as I cannot replicate the bug with your code but feel free to reopen this if needed. Thanks!