Frequently Asked Questions¶
Getting Started¶
What is Bond and how is it different from other Flutter frameworks?¶
Bond is a comprehensive Flutter toolkit that provides a complete development ecosystem rather than just individual packages. Unlike other solutions that focus on single concerns, Bond offers:
- Unified Architecture: All packages work together seamlessly using Service Providers
- Production Ready: Includes everything needed for real apps (analytics, push notifications, caching, etc.)
- Developer Experience: CLI tools, comprehensive docs, and clear conventions
- Type Safety: Everything is typed, from API responses to form validation
Do I need to use all Bond packages?¶
No! Bond is designed for gradual adoption. You can: - Start with just BondFire for networking - Add Bond Forms when you need robust form handling - Use the full starter template for new projects - Mix Bond packages with your existing Flutter packages
How does Bond compare to other state management solutions?¶
Bond doesn't replace state management solutions like Riverpod, BLoC, or GetX. Instead, it provides: - Service Providers for dependency injection and feature organization - Form Controllers that integrate with your chosen state management - Typed networking that works with any state solution - Adapters for popular state management libraries
Architecture¶
Why Service Providers instead of just using GetIt directly?¶
Service Providers provide several benefits over direct GetIt usage:
- Organization: Each feature has its own provider with clear boundaries
- Response Decoding: Shared JSON factories across networking and caching
- Conventions: Consistent patterns across all features
- Testing: Easy to swap implementations for testing
- Lifecycle Management: Proper initialization and cleanup
// Direct GetIt - scattered and hard to maintain
GetIt.instance.registerLazySingleton(() => AuthApi());
GetIt.instance.registerFactory(() => LoginController());
// ... scattered across multiple files
// Bond Service Provider - organized and clear
class AuthServiceProvider extends ServiceProvider with ResponseDecoding {
@override
Future<void> register(GetIt it) async {
it.registerLazySingleton(() => AuthApi(it()));
it.registerFactory(() => LoginController(it()));
}
@override
Map<Type, JsonFactory> get factories => {User: User.fromJson};
}
How do I organize features in larger applications?¶
Bond follows a feature-based architecture:
lib/
├── features/
│ ├── auth/ # Authentication feature
│ ├── posts/ # Posts feature
│ ├── messaging/ # Messaging feature
│ └── profile/ # Profile feature
├── core/ # Shared utilities
└── providers/ # App-level providers
Each feature should: - Have its own Service Provider - Be self-contained with minimal dependencies - Communicate through interfaces or events - Include its own models, APIs, and UI components
Can I use Bond with existing Flutter projects?¶
Yes! Bond supports incremental adoption:
- Start with one package: Add BondFire to replace your existing HTTP client
- Add Service Providers: Gradually organize your dependencies
- Migrate features: Move existing features to Bond's structure
- Use the CLI: Generate new features with Bond conventions
Networking¶
How does BondFire compare to using Dio directly?¶
BondFire is built on Dio but adds:
// Direct Dio - lots of boilerplate
final response = await dio.get('/users');
final users = (response.data['data'] as List)
.map((json) => User.fromJson(json))
.toList();
// BondFire - clean and type-safe
final users = await bondFire
.get<ListResponse<User>>('/users')
.factory(ListResponse<User>.fromJson)
.execute();
Benefits: - Type safety with compile-time checking - Automatic caching with configurable policies - Consistent error handling across all endpoints - Shared JSON factories eliminate duplication
How do I handle authentication with BondFire?¶
Use interceptors for automatic token handling:
class AuthInterceptor extends Interceptor {
final TokenStorage _tokenStorage;
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
final token = await _tokenStorage.getAccessToken();
if (token != null) {
options.headers['Authorization'] = 'Bearer $token';
}
handler.next(options);
}
@override
void onError(DioException err, ErrorInterceptorHandler handler) async {
if (err.response?.statusCode == 401) {
// Token expired, try to refresh
final refreshed = await _refreshToken();
if (refreshed) {
// Retry original request
final response = await _retry(err.requestOptions);
handler.resolve(response);
return;
}
}
handler.next(err);
}
}
What's the best caching strategy for my app?¶
Choose based on your data characteristics:
- Cache-else-network: For data that doesn't change often (user profiles)
- Cache-then-network: For feeds where you want instant loading + fresh data
- Network-else-cache: For critical data where freshness is important
- Network-only: For data that should never be cached (payments, sensitive info)
// User profile - rarely changes
bondFire.get<User>('/me')
.cache(cachePolicy: CachePolicy.cacheElseNetwork)
.execute();
// News feed - want instant loading + fresh content
bondFire.get<ListResponse<Post>>('/posts')
.cache(cachePolicy: CachePolicy.cacheThenNetwork)
.streamExecute();
Forms¶
How do I create complex forms with conditional fields?¶
Use dynamic form state updates:
class DynamicFormController extends FormStateNotifier {
void onAccountTypeChanged(AccountType type) {
final fields = Map<String, FormFieldState>.from(state.fields);
// Remove type-specific fields
fields.removeWhere((key, _) => key.startsWith('business_'));
fields.removeWhere((key, _) => key.startsWith('personal_'));
// Add new type-specific fields
switch (type) {
case AccountType.business:
fields.addAll({
'business_name': TextFieldState('', rules: [Rules.required()]),
'tax_id': TextFieldState('', rules: [Rules.required()]),
});
break;
case AccountType.personal:
fields.addAll({
'personal_id': TextFieldState('', rules: [Rules.required()]),
});
break;
}
updateFormState(state.copyWith(fields: fields));
}
}
How do I validate fields against server data?¶
Create async validation rules:
class UniqueEmailRule extends ValidationRule<String> {
final AuthRepository _authRepository;
@override
Future<bool> validateAsync(String value, Map<String, FormFieldState> fields) async {
if (value.isEmpty) return true; // Let required rule handle this
try {
return await _authRepository.isEmailAvailable(value);
} catch (e) {
return true; // Fail gracefully if server is unreachable
}
}
}
Testing¶
How do I test Service Providers?¶
Use dependency injection to swap implementations:
void main() {
group('AuthServiceProvider', () {
late GetIt testContainer;
setUp(() {
testContainer = GetIt.instance;
// Register test implementations
testContainer.registerLazySingleton<AuthApi>(() => MockAuthApi());
// Register the provider
final provider = AuthServiceProvider();
provider.register(testContainer);
});
tearDown(() {
testContainer.reset();
});
test('should register all dependencies', () {
expect(testContainer.isRegistered<AuthRepository>(), true);
expect(testContainer.isRegistered<LoginFormController>(), true);
});
});
}
How do I test forms?¶
Test controllers independently of widgets:
void main() {
group('LoginFormController', () {
late LoginFormController controller;
late MockAuthRepository mockAuthRepo;
setUp(() {
mockAuthRepo = MockAuthRepository();
controller = LoginFormController(mockAuthRepo);
});
test('should validate email correctly', () {
controller.updateText('email', 'invalid-email');
expect(controller.state.textField('email').isValid, false);
controller.updateText('email', 'test@example.com');
expect(controller.state.textField('email').isValid, true);
});
});
}
Performance¶
My app is slow to start. How can I optimize it?¶
-
Use lazy registration in Service Providers:
-
Defer heavy initialization:
-
Use async singletons for services requiring async setup:
How do I optimize network performance?¶
-
Use appropriate cache policies:
-
Implement request batching:
-
Use connection pooling and HTTP/2.
Deployment¶
How do I set up different environments?¶
Bond uses environment files with flavors:
-
Create environment files:
-
Configure each environment:
-
Run with specific environment:
How do I set up CI/CD for Bond projects?¶
Use the provided GitHub Actions templates:
- Copy CI workflow from the CI/CD guide
- Set up secrets for environment files and signing keys
- Configure deployment targets (Firebase App Distribution, App Store, Play Store)
- Add quality gates for testing and analysis
Troubleshooting¶
I'm getting "Provider not found" errors¶
This usually means a Service Provider isn't registered:
-
Check provider registration in
lib/app/app.dart: -
Verify provider implementation:
My JSON deserialization is failing¶
Check your ResponseDecoding setup:
-
Ensure factories are registered:
-
Verify factory function signature:
My forms aren't validating correctly¶
Common form issues:
-
Check field names match:
-
Ensure rules are appropriate:
My cache isn't working¶
Cache troubleshooting:
-
Check cache keys are consistent:
-
Verify cache duration:
Migration¶
How do I migrate from other packages to Bond?¶
From Dio to BondFire¶
Before:
final response = await dio.get('/users');
final users = (response.data['data'] as List)
.map((json) => User.fromJson(json))
.toList();
After:
final response = await bondFire
.get<ListResponse<User>>('/users')
.factory(ListResponse<User>.fromJson)
.execute();
final users = response.data;
From SharedPreferences to Bond Cache¶
Before:
final prefs = await SharedPreferences.getInstance();
await prefs.setString('user', json.encode(user.toJson()));
final userJson = prefs.getString('user');
final user = userJson != null ? User.fromJson(json.decode(userJson)) : null;
After:
await Cache.put('user', user);
final user = Cache.get<User>('user', fromJsonFactory: User.fromJson);
How do I migrate existing forms to Bond Forms?¶
- Identify form fields and their validation rules
- Create field states:
- Create form controller:
- Update UI to use Bond form widgets or helpers
Best Practices¶
What are the most important Bond conventions to follow?¶
- One Service Provider per feature - keeps boundaries clear
- Use typed responses - prevents runtime errors
- Register JSON factories - enables shared decoding
- Follow feature structure - data/presentation separation
- Test with dependency injection - swap implementations in tests
How should I structure my Bond project for a team?¶
lib/
├── features/ # Business features
│ ├── auth/ # One developer/team
│ ├── posts/ # Another developer/team
│ └── messaging/ # Third developer/team
├── core/ # Shared by all teams
│ ├── services/ # Common services
│ ├── widgets/ # Shared UI components
│ └── utils/ # Utility functions
└── providers/ # App-level configuration
Team Guidelines: - Each team owns specific features - Core changes require review from all teams - Use interfaces for cross-feature communication - Shared components go in core/widgets
What testing strategy should I use?¶
Testing Pyramid: 1. Unit Tests (70%): Test individual classes and functions 2. Widget Tests (20%): Test UI components in isolation 3. Integration Tests (10%): Test complete user flows
Bond-Specific Testing: - Test Service Providers with mock dependencies - Test form controllers with validation scenarios - Test API services with mock responses - Test navigation flows with route guards
Common Patterns¶
How do I implement real-time features?¶
Use WebSockets with Bond's architecture:
class ChatServiceProvider extends ServiceProvider {
@override
Future<void> register(GetIt it) async {
it.registerLazySingleton<WebSocketService>(() => WebSocketService());
it.registerLazySingleton<ChatRepository>(() => ChatRepository(it(), it()));
}
}
class ChatRepository {
final WebSocketService _webSocket;
final ChatApiService _apiService;
Stream<Message> get messageStream => _webSocket.messageStream
.map((data) => Message.fromJson(data));
Future<void> sendMessage(String text) async {
// Send via WebSocket for real-time
_webSocket.send({'type': 'message', 'text': text});
// Also send via API for persistence
await _apiService.sendMessage(text);
}
}
How do I implement offline support?¶
Combine caching with connectivity checking:
class OfflineRepository {
final ApiService _apiService;
final Cache _cache;
final ConnectivityService _connectivity;
Future<List<Post>> getPosts() async {
final isOnline = await _connectivity.isConnected();
if (isOnline) {
try {
final posts = await _apiService.getPosts();
await _cache.put('posts', posts, Duration(hours: 24));
return posts;
} catch (e) {
// Network failed, try cache
final cached = await _cache.get<List<Post>>('posts');
return cached ?? [];
}
} else {
// Offline mode
final cached = await _cache.get<List<Post>>('posts');
return cached ?? [];
}
}
}
Getting Help¶
Where can I get support?¶
- Documentation: Check this comprehensive guide first
- GitHub Issues: Report bugs and request features
- Discord Community: Get help from other developers
- Stack Overflow: Tag questions with
flutter-bond - Email Support: Enterprise customers get priority support
How do I contribute to Bond?¶
- Documentation: Improve guides and add examples
- Bug Reports: File detailed issues with reproduction steps
- Feature Requests: Propose new features with use cases
- Code Contributions: Submit pull requests with tests
- Community: Help other developers in Discord
What's the roadmap for Bond?¶
Check the GitHub roadmap for: - Upcoming features and improvements - Breaking changes and migration guides - Community feature requests - Long-term architectural plans
Enterprise¶
Is Bond suitable for enterprise applications?¶
Yes! Bond is designed for production use with: - Proven architecture used in real applications - Comprehensive testing support - Security best practices built-in - Scalable structure for large teams - Long-term support for stable versions
Do you offer enterprise support?¶
Yes, enterprise support includes: - Priority issue resolution - Architecture consulting - Custom feature development - Training and onboarding - Migration assistance
Contact us at enterprise@bond.dev for more information.
Can I use Bond in regulated industries?¶
Bond supports compliance requirements through: - Security features: Certificate pinning, encryption, secure storage - Audit trails: Comprehensive logging and analytics - Data governance: Clear data flow and storage policies - Testing requirements: Comprehensive test coverage support
Version and Compatibility¶
What Flutter versions does Bond support?¶
Bond supports Flutter 3.10.0 and later. We test against: - Current stable Flutter release - Previous stable release for compatibility - Beta channel for upcoming features
How often does Bond release new versions?¶
- Patch releases (bug fixes): As needed
- Minor releases (new features): Monthly
- Major releases (breaking changes): Every 6-12 months
What's the upgrade path for breaking changes?¶
- Read release notes for migration instructions
- Use migration tools:
bond migrate --from=1.x --to=2.x - Test thoroughly with the new version
- Update incrementally rather than all at once
Troubleshooting¶
My app won't build after updating Bond¶
-
Clean and rebuild:
-
Check for breaking changes in release notes
-
Update imports if package structure changed
-
Run Bond analysis:
Performance is worse after updating¶
- Profile your app to identify bottlenecks
- Check release notes for performance-related changes
- Review caching strategies - defaults may have changed
- Report performance regressions to the Bond team
Still have questions? Join our Discord community or check the GitHub discussions.