Search code examples
flutterios-simulatorbloc

Flutter App that only shows a black screen on iOS Simulator


I've written a Flutter App that use the Bloc Pattern and the GetIt package. It's really weird that the app works fine on Android device but on an iOS Simulator, it only shows a black screen...

Here my code:

main.dart

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );

  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
  AppInitializer.init();

  runApp(App());
}

app_initializer.dart

import 'package:authentication_repository/authentication_repository.dart';
import 'package:get_it/get_it.dart';
import 'package:techniciens/blocs/blocs.dart';
import 'package:user_repository/user_repository.dart';

class AppInitializer {
  static final getIt = GetIt.instance;

  static void init() {
    _registerSingletons();
    _registerBlocSingletons();
  }

  static void _registerSingletons() {
    getIt.registerSingleton(AuthenticationRepository()..user.first);
    getIt.registerLazySingleton<UserRepository>(() => UserRepository());
  }

  static void _registerBlocSingletons() {
    getIt.registerLazySingleton<AuthenticationBloc>(() => AuthenticationBloc(
        authenticationRepository:
            GetIt.instance.get<AuthenticationRepository>()));

    getIt.registerLazySingleton<SettingsFormCubit>(() => SettingsFormCubit());
    getIt.registerLazySingleton<UserInfoCubit>(() => UserInfoCubit());
  }
}

app.dart

import 'package:authentication_repository/authentication_repository.dart';
import 'package:dynamic_color/dynamic_color.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:overlay_support/overlay_support.dart';
import 'package:techniciens/blocs/blocs.dart';
import 'package:techniciens/core/constants/constants.dart';
import 'package:techniciens/screens/screens.dart';
import 'package:get_it/get_it.dart';
import 'package:user_repository/user_repository.dart';

import 'core/theme/app_theme.dart';

class App extends StatelessWidget {
  final AuthenticationRepository _authenticationRepository =
      GetIt.instance.get<AuthenticationRepository>();
  final UserRepository _userRepository = GetIt.instance.get<UserRepository>();
  final AuthenticationBloc _authenticationBloc =
      GetIt.instance.get<AuthenticationBloc>();

  App({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MultiRepositoryProvider(
      providers: [
        RepositoryProvider.value(value: _authenticationRepository),
        RepositoryProvider.value(value: _userRepository),
      ],
      child: BlocProvider<AuthenticationBloc>.value(
        value: _authenticationBloc,
        child: const AppView(),
      ),
    );
  }
}

class AppView extends StatefulWidget {
  const AppView({Key? key}) : super(key: key);

  @override
  State<AppView> createState() => _AppViewState();
}

class _AppViewState extends State<AppView> {
  final _navigatorKey = GlobalKey<NavigatorState>();

  NavigatorState get _navigator => _navigatorKey.currentState!;

  @override
  Widget build(BuildContext context) {
    return DynamicColorBuilder(
      builder: (ColorScheme? lightColorScheme, ColorScheme? darkColorScheme) {
        return OverlaySupport.global(
          child: MaterialApp(
            title: kLoginAppbarTitle,
            theme: AppTheme.lightTheme(lightColorScheme),
            darkTheme: AppTheme.darkTheme(darkColorScheme),
            themeMode: ThemeMode.system,
            debugShowCheckedModeBanner: false,
            navigatorKey: _navigatorKey,
            builder: (context, child) {
              return BlocListener<AuthenticationBloc, AuthenticationState>(
                listener: (_, state) {
                  switch (state.authStatus) {
                    case AuthenticationStatus.unauthenticated:
                      _navigator.pushAndRemoveUntil<void>(
                          LoginPage.route(), (route) => false);
                      break;
                    case AuthenticationStatus.authenticated:
                      _navigator.pushAndRemoveUntil<void>(
                          HomePage.route(), (route) => false);
                      break;
                    default:
                      break;
                  }
                },
                child: child,
              );
            },
            onGenerateRoute: (_) => SplashPage.route(),
          ),
        );
      },
    );
  }
}

splash_page.dart

import 'package:flutter/material.dart';

class SplashPage extends StatelessWidget {
  static Route<void> route() {
    return MaterialPageRoute<void>(builder: (_) => const SplashPage());
  }

  const SplashPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: CircularProgressIndicator.adaptive(strokeWidth: 5),
      ),
    );
  }
}

As I could see in the Flutter Inspector and with some print commands, the apps runs normally until the BlocListener and falls on the onGenerateRoute.

The black screen

It gives me this

Launching lib/main.dart on iPhone 14 Pro in debug mode...
Running Xcode build...
Xcode build done.                                           13,8s
GrMtlCommandBuffer: WARNING: Creating MTLCommandBuffer while in background.
GrMtlCommandBuffer: WARNING: Creating MTLCommandBuffer while in background.
Debug service listening on ws://127.0.0.1:52638/_tIC8TChUyE=/ws
Syncing files to device iPhone 14 Pro...
flutter: dynamic_color: Dynamic color not detected on this device.

I've erased all data on the iOS Simulator, it doesn't give anything...

On Android Simulator, the code works super fine, but here, it fails every time on the Splash Screen.

Thank you for your help!


Solution

  • Enter the Info.plist. Most likely you have added a tag for your application to support multiple scenes (ie multiple windows or screens). It should look like this:

      <key>UIApplicationSceneManifest</key>
      <dict>
          <key>UIScene Settings</key>
          <dict/>
      </dict>
    

    If you find it like this, remove the key that allows multiple windows. UISceneConfigurationsUIApplicationSceneManifest

      <key>UIApplicationSceneManifest</key>
      <dict>
          <key>UIApplicationSupportsMultipleScenes</key>
          <true/>
          <key>UIScene Settings</key>
          <dict/>
      </dict>
    

    this is enough (and you can avoid a weird black screen!)