Development Guide
This guide provides information for developers who want to customize and extend the Martfury Flutter app.
Project Architecture
Clean Architecture
The app follows clean architecture principles with clear separation of concerns:
lib/
├── core/ # Core functionality and configuration
│ ├── app_config.dart # App configuration
│ ├── constants/ # App constants
│ └── utils/ # Utility functions
├── main.dart # Application entry point
└── src/
├── controller/ # Business logic (GetX controllers)
├── model/ # Data models and entities
├── service/ # API services and data providers
├── theme/ # App theme and styling
└── view/ # UI components
├── screen/ # App screens
└── widget/ # Reusable widgets
State Management
The app uses GetX for state management, providing:
- Reactive State Management: Using
Rx
variables - Dependency Injection: With
Get.put()
andGet.find()
- Navigation: Using
Get.to()
and related methods - Internationalization: Built-in localization support
Getting Started with Development
Setting Up Development Environment
- Install Flutter SDK (3.7.2 or higher)
- Install IDE (Android Studio, VS Code, or IntelliJ)
- Clone the project and run
flutter pub get
- Configure environment variables in
.env
- Run the app with
flutter run
Development Workflow
- Create Feature Branch:
git checkout -b feature/new-feature
- Implement Changes: Follow the architecture patterns
- Test Changes: Run tests and manual testing
- Code Review: Submit pull request for review
- Merge: Merge to main branch after approval
Adding New Features
Creating a New Screen
- Create Model (if needed):
dart
// lib/src/model/new_model.dart
class NewModel {
final int id;
final String name;
NewModel({required this.id, required this.name});
factory NewModel.fromJson(Map<String, dynamic> json) {
return NewModel(
id: json['id'],
name: json['name'],
);
}
}
- Create Service:
dart
// lib/src/service/new_service.dart
class NewService {
static Future<List<NewModel>> getData() async {
// API call implementation
}
}
- Create Controller:
dart
// lib/src/controller/new_controller.dart
class NewController extends GetxController {
final RxList<NewModel> items = <NewModel>[].obs;
final RxBool isLoading = false.obs;
@override
void onInit() {
super.onInit();
loadData();
}
Future<void> loadData() async {
isLoading.value = true;
try {
items.value = await NewService.getData();
} catch (e) {
Get.snackbar('Error', 'Failed to load data');
} finally {
isLoading.value = false;
}
}
}
- Create Screen:
dart
// lib/src/view/screen/new_screen.dart
class NewScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetBuilder<NewController>(
init: NewController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(title: Text('New Screen')),
body: Obx(() {
if (controller.isLoading.value) {
return Center(child: CircularProgressIndicator());
}
return ListView.builder(
itemCount: controller.items.length,
itemBuilder: (context, index) {
final item = controller.items[index];
return ListTile(title: Text(item.name));
},
);
}),
);
},
);
}
}
Adding New API Endpoints
- Define API Service:
dart
class ApiEndpoints {
static const String newEndpoint = '/api/new-endpoint';
}
class NewApiService {
static final Dio _dio = ApiService.createDio();
static Future<ApiResponse<List<NewModel>>> getNewData() async {
try {
final response = await _dio.get(ApiEndpoints.newEndpoint);
// Handle response
} catch (e) {
throw ApiException('Failed to fetch data');
}
}
}
- Update Models to match API response structure
- Implement Error Handling for network and API errors
- Add Loading States and user feedback
Implementing Social Login
Adding Google Sign-In
- Add Dependencies:
yaml
dependencies:
google_sign_in: ^6.1.5
firebase_auth: ^4.7.2
- Create Google Auth Service:
dart
class GoogleAuthService {
static final GoogleSignIn _googleSignIn = GoogleSignIn(
scopes: ['email', 'profile'],
);
static Future<GoogleSignInAccount?> signIn() async {
try {
return await _googleSignIn.signIn();
} catch (error) {
print('Google Sign-In Error: $error');
return null;
}
}
static Future<void> signOut() async {
await _googleSignIn.signOut();
}
}
- Integrate with Authentication Controller:
dart
class AuthController extends GetxController {
Future<void> signInWithGoogle() async {
try {
isLoading.value = true;
final googleUser = await GoogleAuthService.signIn();
if (googleUser != null) {
final googleAuth = await googleUser.authentication;
final result = await AuthApiService.socialLogin('google', {
'access_token': googleAuth.accessToken,
'id_token': googleAuth.idToken,
});
if (result.success) {
await _handleSuccessfulLogin(result.data);
}
}
} catch (e) {
Get.snackbar('Error', 'Google Sign-In failed');
} finally {
isLoading.value = false;
}
}
}
Adding Facebook Login
- Add Dependencies:
yaml
dependencies:
flutter_facebook_auth: ^6.0.2
- Create Facebook Auth Service:
dart
class FacebookAuthService {
static Future<LoginResult> signIn() async {
return await FacebookAuth.instance.login(
permissions: ['email', 'public_profile'],
);
}
static Future<void> signOut() async {
await FacebookAuth.instance.logOut();
}
}
Adding Apple Sign-In
- Add Dependencies:
yaml
dependencies:
sign_in_with_apple: ^5.0.0
- Create Apple Auth Service:
dart
class AppleAuthService {
static Future<AuthorizationCredentialAppleID> signIn() async {
return await SignInWithApple.getAppleIDCredential(
scopes: [
AppleIDAuthorizationScopes.email,
AppleIDAuthorizationScopes.fullName,
],
);
}
}
Customization Guide
Theming and Styling
Updating App Colors
Edit lib/src/theme/app_theme.dart
:
dart
class AppTheme {
static const Color primaryColor = Color(0xFF2196F3);
static const Color secondaryColor = Color(0xFF03DAC6);
static const Color backgroundColor = Color(0xFFFAFAFA);
static ThemeData get lightTheme => ThemeData(
primarySwatch: Colors.blue,
primaryColor: primaryColor,
backgroundColor: backgroundColor,
);
}
Custom Widgets
Create reusable widgets in lib/src/view/widget/
:
dart
class CustomButton extends StatelessWidget {
final String text;
final VoidCallback onPressed;
const CustomButton({
Key? key,
required this.text,
required this.onPressed,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: AppTheme.primaryColor,
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
),
child: Text(text),
);
}
}
Adding New Languages
- Create Translation File:
json
// assets/translations/es.json
{
"app": {
"name": "Martfury",
"welcome": "Bienvenido a Martfury"
},
"auth": {
"login": "Iniciar Sesión",
"register": "Registrarse"
}
}
- Update Supported Locales:
dart
// lib/main.dart
supportedLocales: [
Locale('en', 'US'),
Locale('vi', 'VN'),
Locale('es', 'ES'), // Add new locale
],
- Use Translations:
dart
Text('auth.login'.tr())
Testing
Unit Testing
Create tests in test/
directory:
dart
// test/controller/new_controller_test.dart
void main() {
group('NewController', () {
late NewController controller;
setUp(() {
controller = NewController();
});
test('should load data on init', () async {
await controller.loadData();
expect(controller.items.isNotEmpty, true);
});
});
}
Widget Testing
dart
// test/widget/custom_button_test.dart
void main() {
testWidgets('CustomButton should display text', (tester) async {
await tester.pumpWidget(
MaterialApp(
home: CustomButton(
text: 'Test Button',
onPressed: () {},
),
),
);
expect(find.text('Test Button'), findsOneWidget);
});
}
Integration Testing
dart
// integration_test/app_test.dart
void main() {
group('App Integration Tests', () {
testWidgets('should navigate through main flow', (tester) async {
app.main();
await tester.pumpAndSettle();
// Test navigation and user flows
});
});
}
Performance Optimization
Image Optimization
dart
class OptimizedImage extends StatelessWidget {
final String imageUrl;
@override
Widget build(BuildContext context) {
return CachedNetworkImage(
imageUrl: imageUrl,
placeholder: (context, url) => ShimmerWidget(),
errorWidget: (context, url, error) => Icon(Icons.error),
memCacheWidth: 300, // Optimize memory usage
);
}
}
List Performance
dart
class OptimizedList extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
cacheExtent: 1000, // Cache items outside viewport
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index].name),
);
},
);
}
}
Debugging
Debug Tools
- Flutter Inspector: Analyze widget tree
- Network Inspector: Monitor API calls
- Performance Overlay: Check rendering performance
- Memory Profiler: Detect memory leaks
Logging
dart
class Logger {
static void debug(String message) {
if (kDebugMode) {
print('[DEBUG] $message');
}
}
static void error(String message, [dynamic error]) {
if (kDebugMode) {
print('[ERROR] $message: $error');
}
}
}
Building for Production
Android Release
bash
flutter build apk --release
# or
flutter build appbundle --release
iOS Release
bash
flutter build ios --release
Environment Configuration
Create separate environment files:
.env.development
.env.staging
.env.production
Code Quality
Linting
Configure analysis_options.yaml
:
yaml
include: package:flutter_lints/flutter.yaml
linter:
rules:
prefer_const_constructors: true
prefer_const_literals_to_create_immutables: true
avoid_print: true
Code Formatting
bash
dart format lib/
Static Analysis
bash
flutter analyze
Deployment
App Store Deployment
- Configure App Store Connect
- Update version numbers
- Build release version
- Upload to App Store Connect
- Submit for review
Google Play Deployment
- Configure Google Play Console
- Generate signed APK/AAB
- Upload to Google Play Console
- Submit for review
Best Practices
Code Organization
- Follow consistent naming conventions
- Use meaningful variable and function names
- Keep functions small and focused
- Comment complex logic
Error Handling
- Always handle potential errors
- Provide meaningful error messages
- Implement retry mechanisms where appropriate
- Log errors for debugging
Security
- Never store sensitive data in plain text
- Use secure storage for tokens
- Validate all user inputs
- Implement proper authentication
For more development resources, check the Flutter documentation and GetX documentation.