flutter_base_architecture_plugin 0.0.2-dev.5
flutter_base_architecture_plugin: ^0.0.2-dev.5 copied to clipboard
A robust and scalable Flutter plugin that provides a solid foundation for building production-ready apps. Includes API calls, BLoC state management, navigation, dependency injection, and localization [...]
Flutter Base Architecture Plugin #
A robust and scalable Flutter plugin that provides a solid foundation for building production-ready apps. Includes API calls, BLoC state management, navigation, dependency injection, and localization out of the box. Perfect for developers looking to save time and follow best practices in Flutter app development.
🚀 Features #
- BLoC State Management: Built-in base classes for BLoC pattern implementation
- API Integration: Pre-configured REST API client with Dio
- Dependency Injection: Singleton-based DI system for plugin services
- Localization: Multi-language support with easy localization management
- Navigation: Enhanced navigation extensions with authentication checks
- Error Handling: Comprehensive error handling and network monitoring
- Logging: Configurable logging with interceptors
- Network Info: Real-time connectivity status monitoring
- Base Components: Reusable base classes for screens, states, and controllers
📦 Installation #
Add this to your package's pubspec.yaml
file:
dependencies:
flutter_base_architecture_plugin:
path: ../flutter_base_architecture_plugin # For local development
# git: https://github.com/your-repo/flutter_base_architecture_plugin.git # For git dependency
For dependency injection in your app, also add:
dependencies:
get_it: ^7.6.0
injectable: ^2.3.2
dev_dependencies:
injectable_generator: ^2.4.1
build_runner: ^2.4.7
🛠️ Setup #
1. Initialize the Plugin #
In your main.dart
:
import 'package:flutter/material.dart';
import 'package:flutter_base_architecture_plugin/core/base_state.dart';
import 'your_injector.dart'; // Your app's injector
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Setup dependency injection
await Injector.setup();
runApp(const MyApp());
}
2. Create Your App's Injector #
Create lib/inject/injector.dart
:
import 'package:flutter_base_architecture_plugin/inject/base_injector.dart';
import 'package:flutter_base_architecture_plugin/base_arch_controller/base_arch_controller.dart';
import 'package:flutter_base_architecture_plugin/imports/core_imports.dart';
import 'package:flutter_base_architecture_plugin/services/app_routes/app_routes_service.dart';
import 'package:flutter_base_architecture_plugin/services/localization/localization_service.dart';
import 'package:flutter_base_architecture_plugin/core/network/network_info.dart';
import 'package:get_it/get_it.dart';
import 'package:injectable/injectable.dart';
import '../api/home/home_api/home_api.dart';
import '../service/home/home_service.dart';
import 'injector.config.dart';
final getIt = GetIt.instance;
@InjectableInit()
void configureDependencies() => getIt.init();
// Module to register plugin and app-specific dependencies
@module
abstract class PluginModule {
@singleton
HomeApi get homeApi => HomeApi(BaseInjector.restApiClient);
@singleton
HomeService get homeService => HomeService(getIt<HomeApi>());
@singleton
LocalizationService get localizationService =>
BaseInjector.localizationService;
@singleton
AppRoutesService get appRoutesService => BaseInjector.appRoutesService;
@singleton
BaseArchController get baseArchController =>
BaseInjector.baseArchController;
@singleton
EventBus get eventBus => BaseInjector.eventBus;
@singleton
NetworkInfoImpl get networkInfoImpl =>
BaseInjector.networkInfoImpl;
}
class Injector {
static Future<bool> setup() async {
// Configure GetIt dependencies
configureDependencies();
// Set external resolver for BaseState
BaseState.setExternalResolver(Injector.resolve);
return true;
}
static T resolve<T extends Object>() => getIt<T>();
}
3. Generate Dependency Injection Code #
Run the code generator:
dart run build_runner build
4. Configure Your App #
class MyApp extends StatefulWidget {
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends BaseState<YourMainBloc, MyApp> {
@override
void initState() {
super.initState();
// Configure the base architecture
bloc.add(SetRestApiConfiguration(
baseUrl: 'https://your-api.com/api/',
connectTimeout: 30,
receiveTimeout: 30,
));
bloc.add(SetLocalizationEvent(
localizationList: [
EnglishLocalization(),
// Add more localizations
],
));
bloc.add(SetProtectedRoutesEvent(
isAuthenticated: false, // Set based on your auth state
protectedRoutesList: [
'/profile',
'/settings',
// Add protected routes
],
));
}
@override
Widget build(BuildContext context) {
return BlocProvider<YourMainBloc>(
create: (context) => bloc,
child: BlocBuilder<YourMainBloc, YourMainData>(
builder: (context, state) {
return MaterialApp(
title: 'Your App',
home: YourHomeScreen(),
// Configure other MaterialApp properties
);
},
),
);
}
}
📱 Usage Examples #
Creating a BLoC #
import 'package:injectable/injectable.dart';
import 'package:flutter_base_architecture_plugin/imports/core_imports.dart';
@injectable
class HomeBloc extends BaseBloc<HomeEvent, HomeData> {
HomeBloc(this._yourService, this._localizationService) : super(initState) {
on<LoadDataEvent>(_loadData);
}
final YourService _yourService;
final LocalizationService _localizationService;
static HomeData get initState => HomeDataBuilder()
..state = ScreenState.loading
..build();
Future<void> _loadData(LoadDataEvent event, Emitter<HomeData> emit) async {
try {
emit(state.rebuild((b) => b..state = ScreenState.loading));
final data = await _yourService.fetchData();
emit(state.rebuild((b) => b
..state = ScreenState.content
..data = data));
} catch (error) {
emit(state.rebuild((b) => b
..state = ScreenState.error
..errorMessage = error.toString()));
}
}
}
Creating a Screen #
class HomeScreen extends StatefulWidget {
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends BaseState<HomeBloc, HomeScreen> {
@override
void initState() {
super.initState();
bloc.add(LoadDataEvent());
}
@override
Widget build(BuildContext context) {
return BlocProvider<HomeBloc>(
create: (context) => bloc,
child: BlocBuilder<HomeBloc, HomeData>(
builder: (context, state) {
switch (state.state) {
case ScreenState.loading:
return const Center(child: CircularProgressIndicator());
case ScreenState.content:
return _buildContent(state);
case ScreenState.error:
return _buildError(state);
default:
return Container();
}
},
),
);
}
Widget _buildContent(HomeData state) {
return Scaffold(
appBar: AppBar(title: Text('Home')),
body: Column(
children: [
// Your content here
ElevatedButton(
onPressed: () => context.pushNamed('/details'),
child: Text('Navigate to Details'),
),
],
),
);
}
Widget _buildError(HomeData state) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Error: ${state.errorMessage}'),
ElevatedButton(
onPressed: () => bloc.add(LoadDataEvent()),
child: Text('Retry'),
),
],
),
),
);
}
}
API Integration #
@injectable
class YourApi {
YourApi(this._restApiClient);
final RestApiClient _restApiClient;
Future<List<YourModel>> fetchData() async {
final response = await _restApiClient.request(
path: '/data',
requestMethod: RequestMethod.GET,
data: RequestData(),
);
if (response.data != null) {
return (response.data!['results'] as List)
.map((json) => YourModel.fromJson(json))
.toList();
}
throw Exception('Failed to fetch data');
}
}
Navigation with Authentication #
// Navigate with automatic auth check
context.pushNamedWithAuthCheck('/protected-route');
// Regular navigation
context.pushNamed('/public-route');
// Navigation with parameters
context.pushNamed('/details', arguments: {'id': 123});
Localization #
// Create your localization class
class AppEnglishLocalization extends BaseLocalization {
@override
String get appName => 'My App';
@override
String get welcome => 'Welcome';
// Add more translations
}
// Use in widgets
Text(LocalizationService.currentLocalization().welcome)
🏗️ Architecture Overview #
├── Core Components
│ ├── BaseBloc - Base class for all BLoCs
│ ├── BaseState - Base class for all StatefulWidgets
│ ├── BaseArchController - Main app controller
│ └── Event Bus - App-wide event communication
│
├── Network Layer
│ ├── RestApiClient - HTTP client wrapper
│ ├── NetworkInfo - Connectivity monitoring
│ └── Error Handling - Centralized error management
│
├── Dependency Injection
│ ├── BaseInjector - Plugin's DI system (Singleton-based)
│ └── External Injector - App's DI system (GetIt Injectable)
│
├── Services
│ ├── LocalizationService - Multi-language support
│ ├── AppRoutesService - Route management
│ └── Custom Services - Your app-specific services
│
└── Extensions
├── Navigation Extensions - Enhanced navigation
├── Context Extensions - Utility extensions
└── String Extensions - String utilities
🔧 Configuration #
REST API Configuration #
bloc.add(SetRestApiConfiguration(
baseUrl: 'https://api.example.com/',
connectTimeout: 30,
receiveTimeout: 30,
defaultHeaders: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
));
Protected Routes #
bloc.add(SetProtectedRoutesEvent(
isAuthenticated: userIsLoggedIn,
protectedRoutesList: [
'/profile',
'/settings',
'/admin',
],
));
🚧 Migration from Kiwi to GetIt Injectable #
If you're migrating from an older version that used Kiwi, follow these steps:
- Remove Kiwi dependencies from your app's
pubspec.yaml
- Add GetIt dependencies as shown in installation
- Update your injector to use the new pattern shown above
- Run code generation with
dart run build_runner build
- Update BaseState initialization in your main.dart
The plugin now uses a singleton-based approach internally while supporting modern GetIt injectable for app-level dependencies.
📚 Additional Resources #
📄 License #
This project is licensed under the MIT License - see the LICENSE file for details.