Skip to content

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

Description

@Jonny1987

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 #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: #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

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions