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
933 stars 343 forks source link

StreamMessageInput causing error "Could not find a Portal above this PortalTarget(debugName: null, portalCandidateLabels=[PortalLabel.main])" #1924

Closed Jonny1987 closed 5 months ago

Jonny1987 commented 5 months ago

Which packages are you using?

stream_chat_flutter

On what platforms did you experience the issue?

Android

What version are you using?

7.1.0 (flutter 3.22.0)

What happened?

The following PortalNotFoundError was thrown building _PortalTargetVisibilityBuilder(visible: false, closeDuration: null, builder: Closure: (BuildContext, bool) => Widget, dirty, state: _PortalTargetVisibilityBuilderState#6a473):
Error: Could not find a Portal above this PortalTarget(debugName: null, portalCandidateLabels=[PortalLabel.main]).

The relevant error-causing widget was:
    StreamMessageInput StreamMessageInput:file:///home/john/dev/WorkWith/lib/src/features/chat/presentation/channel_page/channel_page.dart:27:17

When the exception was thrown, this was the stack:
#0      _PortalTargetState.build.<anonymous closure> (package:flutter_portal/src/portal_target.dart:265:11)
portal_target.dart:265
#1      _PortalTargetVisibilityBuilderState.build (package:flutter_portal/src/portal_target.dart:449:26)
portal_target.dart:449
#2      StatefulElement.build (package:flutter/src/widgets/framework.dart:5599:27)
framework.dart:5599

Steps to reproduce

Here is my code (this code is exactly the same as the code in https://github.com/GetStream/stream-chat-flutter/issues/1923 but with the app_bar removed from channel_page.dart and StreamMessageInput added):

gorouter:

      GoRoute(
        path: '/home',
        name: AppRoute.home.name,
        builder: (context, state) {
          final tabName = state.uri.queryParameters['tab'];
          final tab =
              tabName == null ? TabsEnum.venues : getTabFromString(tabName);
          return StreamChat(
            client: streamChatClient,
            child: HomePage(
              tab: tab,
            ),
          );
        },
        routes: [
          GoRoute(
            path: 'chat',
            name: AppRoute.venueChat.name,
            builder: (context, state) {
              Channel channel = state.extra as Channel;
              return StreamChannel(
                channel: channel,
                child: const ChannelPage(),
              );
            },
          ),
        ],
      )

chat_tab.dart (as a tab in BottomNavigationBar):

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:workwith/src/features/auth/data/auth_repository.dart';
import 'package:workwith/src/features/chat/presentation/chat_tab/channel_list.dart';
import 'package:workwith/src/features/profile/domain/profile_model.dart';
import 'package:workwith/src/features/profile/presentation/profile_tab/profile_tab_controller.dart';
import 'package:workwith/src/utils/constants.dart';

class ChatTab extends ConsumerStatefulWidget {
  const ChatTab({super.key});

  @override
  ConsumerState<ChatTab> createState() => _ChatTabState();
}

class _ChatTabState extends ConsumerState<ChatTab> {
  OwnUser? ownUser;

  Future<void> connectUser(Profile profile) async {
    if (ownUser != null) {
      return;
    }
    final user = User(
      id: ref.read(authRepositoryProvider).currentUserId,
      extraData: {
        'username': profile.username!,
        'image': profile.profilePhotoUrl,
      },
    );
    ownUser = await streamChatClient.connectUser(
      user,
      streamChatClient.devToken(profile.username!).rawValue,
    );
  }

  @override
  Widget build(BuildContext context) {
    final profile = ref
        .watch(profileTabControllerProvider
            .select((value) => value.profileGetStatus))
        .value;
    if (profile == null) {
      return const Center(child: CircularProgressIndicator());
    } else {
      return FutureBuilder(
        future: connectUser(profile),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          } else {
            return ChannelListPage(client: streamChatClient);
          }
        },
      );
    }
  }
}

channel_list.dart:

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:workwith/src/logger.dart';
import 'package:workwith/src/routing/app_router.dart';

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

  final StreamChatClient client;

  @override
  State<ChannelListPage> createState() => _ChannelListPageState();
}

class _ChannelListPageState extends State<ChannelListPage> {
  late final _controller = StreamChannelListController(
    client: widget.client,
    filter: Filter.in_(
      'members',
      [StreamChat.of(context).currentUser!.id],
    ),
    channelStateSort: const [SortOption('last_message_at')],
  );

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => Scaffold(
        body: RefreshIndicator(
          onRefresh: _controller.refresh,
          child: StreamChannelListView(
            controller: _controller,
            onChannelTap: (channel) {
              logger().w('Channel tapped: ${channel.cid}');
              context.pushNamed(AppRoute.venueChat.name, extra: channel);
            },
          ),
        ),
      );
}

channel_page.dart:

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

class ChannelPage extends StatelessWidget {
  const ChannelPage({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[
          Expanded(
            child: StreamMessageListView(
              messageBuilder:
                  (context, details, messageList, defaultMessageWidget) {
                return defaultMessageWidget.copyWith(
                  showThreadReplyIndicator: false,
                  showUserAvatar: DisplayWidget.show,
                  reverse: true,
                );
              },
            ),
          ),
          const StreamMessageInput(),
        ],
      ),
    );
  }
}

Also in main.dart I wrap the MaterialApp.router with StreamChatTheme (following the last comment here: https://github.com/GetStream/stream-chat-flutter/issues/1892)

Supporting info to reproduce

Removing StreamMessageInput stops the error from occurring

Relevant log output

No response

Flutter analyze output

No issues

Flutter doctor output

[✓] Flutter (Channel stable, 3.22.0, on Ubuntu 22.04.4 LTS 6.5.0-28-generic, locale en_US.UTF-8)
    • Flutter version 3.22.0 on channel stable at /home/john/dev/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 5dcb86f68f (12 days ago), 2024-05-09 07:39:20 -0500
    • Engine revision f6344b75dc
    • Dart version 3.4.0
    • DevTools version 2.34.3

[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    • Android SDK at /home/john/Android/Sdk
    • Platform android-34, build-tools 34.0.0
    • Java binary at: /opt/android-studio/jbr/bin/java
    • Java version OpenJDK Runtime Environment (build 17.0.7+0-17.0.7b1000.6-10550314)
    • All Android licenses accepted.

[✓] Chrome - develop for the web
    • Chrome at google-chrome

[✓] Linux toolchain - develop for Linux desktop
    • Ubuntu clang version 14.0.0-1ubuntu1.1
    • cmake version 3.22.1
    • ninja version 1.10.1
    • pkg-config version 0.29.2

[✓] Android Studio (version 2023.1)
    • Android Studio at /opt/android-studio
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 17.0.7+0-17.0.7b1000.6-10550314)

[✓] VS Code (version 1.88.1)
    • VS Code at /usr/share/code
    • Flutter extension version 3.88.0

[✓] Connected device (3 available)
    • sdk gphone x86 64 (mobile) • emulator-5554 • android-x64    • Android 13 (API 33) (emulator)
    • Linux (desktop)            • linux         • linux-x64      • Ubuntu 22.04.4 LTS 6.5.0-28-generic
    • Chrome (web)               • chrome        • web-javascript • Google Chrome 123.0.6312.86

[✓] Network resources
    • All expected network resources are available.

• No issues found!

Code of Conduct

Jonny1987 commented 5 months ago

Ok I feel stupid, I fixed this by wrapping my chat page in gorouter with StreamChat and also the same with the ChannelListPage in chat_tab.dat, and removed the StreamChat from the home route in gorouter