Skip to content

Feature -> Develop | Added BLoc State Management #48

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 73 additions & 9 deletions lib/app_config.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,86 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:sentry_flutter/sentry_flutter.dart';

import 'routes/middleware.dart';
import 'vaahextendflutter/app_theme.dart';
import 'vaahextendflutter/env/env.dart';
import 'vaahextendflutter/base/base_bloc/base_bloc.dart';
import 'vaahextendflutter/env/env_bloc/env_bloc.dart';
import 'vaahextendflutter/helpers/constants.dart';
import 'vaahextendflutter/widgets/debug.dart';

final _navigatorKey = GlobalKey<NavigatorState>();
final navigatorKey = GlobalKey<NavigatorState>();

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

@override
Widget build(BuildContext context) {
return BlocBuilder<BaseBloc, BaseBlocState>(
bloc: BaseBloc.instance,
buildWhen: (previous, current) {
if (previous is BaseBlocLoaded && current is BaseBlocLoaded) {
return previous.app != current.app;
}
return current is BaseBlocLoaded || current is BaseBlocError;
},
builder: (context, state) {
if (state is BaseBlocInitial) {
return _initialStateView();
} else if (state is BaseBlocLoading) {
return _loadingStateView();
} else if (state is BaseBlocLoaded) {
return state.app;
} else if (state is BaseBlocError) {
return state.errorApp;
}
return ErrorAppConfig();
},
);
}

Widget _initialStateView() {
return Center(
child: Column(
children: [
Text(
'Initial state of Base Bloc',
),
verticalMargin4,
ElevatedButton(
onPressed: () => BaseBloc.instance.add(
InitializeApp(
app: AppConfig(),
errorApp: ErrorAppConfig(),
),
),
child: Text('Tap to load'),
)
],
),
);
}

Widget _loadingStateView() {
return const MaterialApp(
home: Scaffold(
body: Center(
child: CupertinoActivityIndicator(),
),
),
);
}
}

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

@override
Widget build(BuildContext context) {
EnvironmentConfig env = EnvironmentConfig.getConfig;
return GetMaterialApp(
title: env.appTitle,
return MaterialApp(
navigatorKey: navigatorKey,
title: EnvBloc.instance.config.appTitle,
theme: ThemeData(
primarySwatch: AppTheme.colors['primary'],
),
Expand All @@ -26,8 +90,8 @@ class AppConfig extends StatelessWidget {
onGenerateRoute: routeMiddleware,
builder: (BuildContext context, Widget? child) {
return DebugWidget(
navigatorKey: _navigatorKey,
child: child!,
navigatorKey: navigatorKey,
child: child ?? const SizedBox.shrink(),
);
},
);
Expand All @@ -39,7 +103,7 @@ class ErrorAppConfig extends StatelessWidget {

@override
Widget build(BuildContext context) {
return GetMaterialApp(
return MaterialApp(
theme: ThemeData(
primarySwatch: AppTheme.colors['primary'],
),
Expand Down
18 changes: 11 additions & 7 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';

import 'app_config.dart';
import 'vaahextendflutter/base/base_controller.dart';
import 'vaahextendflutter/base/base_bloc/base_bloc.dart';

Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
BaseController baseController = Get.put(BaseController());
await baseController.init(
app: const AppConfig(),
errorApp: const ErrorAppConfig(),
); // Pass main app as argument in init method

/// Initialize the app's with core features
BaseBloc.instance.add(
InitializeApp(
app: AppConfig(),
errorApp: const ErrorAppConfig(),
),
);

runApp(AppLauncher());
}
131 changes: 131 additions & 0 deletions lib/vaahextendflutter/base/base_bloc/base_bloc.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/widgets.dart';
import 'package:get_storage/get_storage.dart';

import '../../app_theme.dart';
import '../../env/env_bloc/env_bloc.dart';
import '../../services/api.dart';
import '../../services/logging_library/logging_library.dart';
import '../../services/notification/internal/notification.dart';
import '../../services/notification/push/notification.dart';
import '../root_asset_bloc/root_asset_bloc.dart';

part 'base_bloc_event.dart';
part 'base_bloc_state.dart';

class BaseBloc extends Bloc<BaseBlocEvent, BaseBlocState> {
BaseBloc._() : super(BaseBlocInitial()) {
on<InitializeApp>(_handleAppInitialization);
}

static final BaseBloc _instance = BaseBloc._();

static BaseBloc get instance => _instance;

Future<void> _initializeStorage() async {
await GetStorage.init();
}

void _initializeEnvironment() {
EnvBloc.instance.add(LoadEnvironment());
}

void _initializeServices() {
AppTheme.init();
Api.init();
}

void _loadUserFromStorage() {
RootAssetBloc.instance.add(LoadUser());
}

Future<void> _initializeNotifications() async {
await PushNotifications.init();
await InternalNotifications.init();
PushNotifications.askPermission();
}

Future<void> _handleAppInitialization(
InitializeApp event,
Emitter<BaseBlocState> emit,
) async {
emit(BaseBlocLoading());

try {
await _initializeStorage();

_initializeEnvironment();

// Todo : Depends on datadog implementation PR
// Initialization of Firebase and Services
// if (firebaseOptions != null) {
// await Firebase.initializeApp(
// options: firebaseOptions,
// );
// }

_initializeServices();

_loadUserFromStorage();

_initializeNotifications();

// Todo : Initialization of Logging Services will be added once Datadog PR proceed
// Todo : it will return a Widget which will be passed in BaseBlocLoaded State
// Todo : Do not remove the below code
// Sentry Initialization (And/ Or) Running main app
// if (null != config.sentryConfig && config.sentryConfig!.dsn.isNotEmpty) {
// await SentryFlutter.init(
// (options) => options
// ..dsn = config.sentryConfig!.dsn
// ..autoAppStart = config.sentryConfig!.autoAppStart
// ..tracesSampleRate = config.sentryConfig!.tracesSampleRate
// ..enableAutoPerformanceTracing = config.sentryConfig!.enableAutoPerformanceTracing
// ..enableUserInteractionTracing = config.sentryConfig!.enableUserInteractionTracing
// ..environment = config.envType,
// );
// Widget child = event.app;
// if (config.sentryConfig!.enableUserInteractionTracing) {
// child = SentryUserInteractionWidget(
// child: child,
// );
// }
// if (config.sentryConfig!.enableAssetsInstrumentation) {
// child = DefaultAssetBundle(
// bundle: SentryAssetBundle(
// enableStructuredDataTracing: true,
// ),
// child: child,
// );
// }
// // Running main app
// runApp(child);
// } else {
// // Running main app when sentry config is not there
// runApp(app);
// }

emit(
BaseBlocLoaded(app: event.app),
);
} catch (e, stackTrace) {
emit(
BaseBlocError(
errorApp: event.errorApp,
errorMessage: e.toString(),
),
);

// Logs an error to LoggingService[Sentry || datadog || firebase] opted by user
// This will log to the remote as well as local based on your ENV config
Log.exception(
e.toString(),
stackTrace: stackTrace,
);
}
}
}
15 changes: 15 additions & 0 deletions lib/vaahextendflutter/base/base_bloc/base_bloc_event.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
part of 'base_bloc.dart';

abstract class BaseBlocEvent {}

class InitializeApp extends BaseBlocEvent {
final Widget app;
final Widget errorApp;
final FirebaseOptions? firebaseOptions;

InitializeApp({
required this.app,
required this.errorApp,
this.firebaseOptions,
});
}
35 changes: 35 additions & 0 deletions lib/vaahextendflutter/base/base_bloc/base_bloc_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
part of 'base_bloc.dart';

abstract class BaseBlocState extends Equatable {
const BaseBlocState();
@override
List<Object?> get props => [];
}

class BaseBlocInitial extends BaseBlocState {}

class BaseBlocLoading extends BaseBlocState {}

class BaseBlocLoaded extends BaseBlocState {
final Widget app;

const BaseBlocLoaded({
required this.app,
});

@override
List<Object?> get props => [app];
}

class BaseBlocError extends BaseBlocState {
final Widget errorApp;
final String errorMessage;

const BaseBlocError({
required this.errorApp,
required this.errorMessage,
});

@override
List<Object?> get props => [errorApp, errorMessage];
}
Loading