slogan3

Ontwikkel een Flutter-app met geavanceerde integratie van externe API's en netwerkbeheer

description

Met deze prompt kan de gebruiker leren hoe hij meerdere externe API's effectief kan integreren in Flutter-apps, met een focus op robuust netwerkbeheer en foutafhandeling. Dit verbetert de betrouwbaarheid en prestaties van de app aanzienlijk, biedt een betere gebruikerservaring en onderscheidt zich van basisintegraties door geavanceerde caching en foutafhandelingsstrategieën.

prompt

Help me een Flutter-app te ontwikkelen die efficiënt meerdere externe API's integreert en robuust netwerkbeheer gebruikt. Ik wil ondersteuning voor functies zoals <beschrijf de gewenste API-functionaliteiten> en een betrouw ...

try_prompt

generate_helper
disclaimerOnPageApi

ai_answers

provider: openai
model: gpt-4.1-nano
Natuurlijk! Hier is een uitgebreide gids voor het ontwikkelen van een Flutter-app die meerdere externe API's efficiënt integreert met robuust netwerkbeheer, inclusief voorbeelden en best practices. --- ### 1. Structuur van API-aanroepen Gebruik een aparte serviceklasse voor API-interacties, bij voorkeur met het `http`-pakket of een geavanceerdere oplossing zoals `dio`. Hiermee scheid je netwerklogica van de UI. **Voorbeeld: API Service met `dio`** ```dart import 'package:dio/dio.dart'; class ApiService { final Dio _dio; ApiService() : _dio = Dio(BaseOptions( baseUrl: 'https://api.example.com/', connectTimeout: 5000, receiveTimeout: 3000, )) { // Optioneel: interceptors voor logging en foutafhandeling _dio.interceptors.add(LogInterceptor(responseBody: true)); } Future<Map<String, dynamic>> fetchProductCatalog() async { try { final response = await _dio.get('/products'); return response.data; } on DioError catch (e) { throw _handleDioError(e); } } Future<Map<String, dynamic>> placeOrder(Map<String, dynamic> orderData) async { try { final response = await _dio.post('/orders', data: orderData); return response.data; } on DioError catch (e) { throw _handleDioError(e); } } // Foutafhandelingsfunctie Exception _handleDioError(DioError error) { if (error.type == DioErrorType.connectTimeout || error.type == DioErrorType.receiveTimeout) { return Exception('Netwerk timeout. Controleer je verbinding.'); } else if (error.response != null) { return Exception('Server fout: ${error.response?.statusCode}'); } else { return Exception('Onbekende netwerkfout.'); } } } ``` --- ### 2. Caching implementeren Voor betere prestaties en gebruikerservaring kun je caching toepassen. Een eenvoudige aanpak is om data lokaal op te slaan met bijvoorbeeld `shared_preferences` of `hive`. Voor meer geavanceerde caching kun je een dedicated cache-bibliotheek gebruiken. **Voorbeeld: Basis caching met `shared_preferences`** ```dart import 'package:shared_preferences/shared_preferences.dart'; class CacheService { Future<void> saveProductCatalog(List<dynamic> products) async { final prefs = await SharedPreferences.getInstance(); prefs.setString('productCatalog', jsonEncode(products)); } Future<List<dynamic>?> getProductCatalog() async { final prefs = await SharedPreferences.getInstance(); final jsonString = prefs.getString('productCatalog'); if (jsonString != null) { return jsonDecode(jsonString); } return null; } } ``` **Gebruik in API Service:** ```dart import 'dart:convert'; class ProductRepository { final ApiService apiService; final CacheService cacheService; ProductRepository({required this.apiService, required this.cacheService}); Future<List<dynamic>> getProductCatalog() async { // Probeer eerst cache final cachedData = await cacheService.getProductCatalog(); if (cachedData != null) { return cachedData; } // Anders API-oproep final data = await apiService.fetchProductCatalog(); await cacheService.saveProductCatalog(data['products']); return data['products']; } } ``` --- ### 3. Netwerkfouten afhandelen Gebruik try-catch blokken en geef gebruikers duidelijke feedback. Je kunt ook een globale foutafhandelingsstrategie implementeren. **Voorbeeld: Netwerkfout tonen in UI** ```dart class ProductPage extends StatefulWidget { @override _ProductPageState createState() => _ProductPageState(); } class _ProductPageState extends State<ProductPage> { late ProductRepository _repository; List<dynamic>? _products; String? _errorMessage; @override void initState() { super.initState(); _repository = ProductRepository( apiService: ApiService(), cacheService: CacheService(), ); _loadProducts(); } void _loadProducts() async { try { final products = await _repository.getProductCatalog(); setState(() { _products = products; _errorMessage = null; }); } catch (e) { setState(() { _errorMessage = e.toString(); }); } } @override Widget build(BuildContext context) { if (_errorMessage != null) { return Center(child: Text('Fout: $_errorMessage')); } if (_products == null) { return Center(child: CircularProgressIndicator()); } return ListView.builder( itemCount: _products!.length, itemBuilder: (context, index) { final product = _products![index]; return ListTile( title: Text(product['name']), ); }, ); } } ``` --- ### 4. Best practices - **Scheiding van concerns:** Houd API-logica gescheiden van UI. - **Asynchrone verwerking:** Gebruik `async/await` en zorg voor loading indicators. - **Foutafhandeling:** Geef gebruikers duidelijke en vriendelijke foutmeldingen. - **Netwerkbeheer:** Gebruik interceptors voor logging en het detecteren van offline status. - **Caching:** Cache belangrijke data en ververs deze op de achtergrond indien nodig. - **Authenticatie:** Behandel tokens veilig, bijvoorbeeld met `flutter_secure_storage`. - **Gebruik Provider, Riverpod of Bloc:** Voor state management en het beheer van API-data. --- Met deze aanpak zorg je voor een efficiënte, robuuste en gebruikersvriendelijke Flutter-app die meerdere API's integreert en goed omgaat met netwerkproblemen.
provider: deepseek
model: deepseek-chat
Zeker! Hier een uitgebreide uitleg voor het ontwikkelen van een robuuste Flutter-app met meerdere API-integraties: ## 1. Projectstructuur en Dependencies Voeg eerst de benodigde dependencies toe aan `pubspec.yaml`: ```yaml dependencies: flutter: sdk: flutter http: ^1.1.0 dio: ^5.0.0 shared_preferences: ^2.2.2 connectivity_plus: ^5.0.1 cached_network_image: ^3.3.0 dev_dependencies: build_runner: ^2.4.0 retrofit: ^4.0.0 json_annotation: ^4.8.1 ``` ## 2. Netwerklaag met Dio en Retrofit ### API Client Setup ```dart // lib/core/network/api_client.dart import 'package:dio/dio.dart'; import 'package:retrofit/retrofit.dart'; import 'package:json_annotation/json_annotation.dart'; part 'api_client.g.dart'; @RestApi(baseUrl: "https://api.jouw-domein.com/v1/") abstract class ApiClient { factory ApiClient(Dio dio, {String baseUrl}) = _ApiClient; @POST("/auth/login") Future<LoginResponse> login(@Body() LoginRequest request); @GET("/products") Future<ProductListResponse> getProducts( @Query("page") int page, @Query("limit") int limit, ); @POST("/orders") Future<OrderResponse> placeOrder(@Body() OrderRequest request); @GET("/orders/{id}") Future<OrderDetails> getOrder(@Path("id") String orderId); } ``` ### Dio Configuration ```dart // lib/core/network/dio_provider.dart import 'package:dio/dio.dart'; import 'package:shared_preferences/shared_preferences.dart'; class DioProvider { static Dio createDio() { final dio = Dio(BaseOptions( connectTimeout: const Duration(seconds: 30), receiveTimeout: const Duration(seconds: 30), sendTimeout: const Duration(seconds: 30), )); // Interceptors voor logging en auth dio.interceptors.add(LogInterceptor( requestBody: true, responseBody: true, )); dio.interceptors.add(AuthInterceptor()); dio.interceptors.add(RetryInterceptor()); return dio; } } class AuthInterceptor extends Interceptor { @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) async { final prefs = await SharedPreferences.getInstance(); final token = prefs.getString('auth_token'); 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 is verlopen, redirect naar login // Eventueel refresh token logica hier } handler.next(err); } } ``` ## 3. Repository Pattern voor API Management ```dart // lib/repositories/auth_repository.dart class AuthRepository { final ApiClient _apiClient; final SharedPreferences _prefs; AuthRepository(this._apiClient, this._prefs); Future<Result<User>> login(String email, String password) async { try { final response = await _apiClient.login( LoginRequest(email: email, password: password), ); await _prefs.setString('auth_token', response.token); await _prefs.setString('user_id', response.user.id); return Result.success(response.user); } on DioException catch (e) { return Result.failure(_handleDioError(e)); } catch (e) { return Result.failure(AppError.unknown()); } } AppError _handleDioError(DioException error) { switch (error.type) { case DioExceptionType.connectionTimeout: case DioExceptionType.sendTimeout: case DioExceptionType.receiveTimeout: return AppError.network(); case DioExceptionType.badResponse: final statusCode = error.response?.statusCode; if (statusCode == 401) return AppError.unauthorized(); if (statusCode == 404) return AppError.notFound(); return AppError.server(); default: return AppError.unknown(); } } } ``` ## 4. Caching Implementatie ### Memory Cache ```dart // lib/core/cache/memory_cache.dart class MemoryCache { final _cache = <String, CacheItem>{}; void set(String key, dynamic data, {Duration? ttl}) { _cache[key] = CacheItem( data: data, expiry: ttl != null ? DateTime.now().add(ttl) : null, ); } dynamic get(String key) { final item = _cache[key]; if (item == null) return null; if (item.expiry != null && item.expiry!.isBefore(DateTime.now())) { _cache.remove(key); return null; } return item.data; } void remove(String key) => _cache.remove(key); void clear() => _cache.clear(); } class CacheItem { final dynamic data; final DateTime? expiry; CacheItem({required this.data, this.expiry}); } ``` ### Cached Repository ```dart // lib/repositories/product_repository.dart class ProductRepository { final ApiClient _apiClient; final MemoryCache _cache; ProductRepository(this._apiClient, this._cache); Future<Result<List<Product>>> getProducts({int page = 1, bool forceRefresh = false}) async { final cacheKey = 'products_page_$page'; // Return cached data if available and not forcing refresh if (!forceRefresh) { final cached = _cache.get(cacheKey); if (cached != null) { return Result.success(cached); } } try { final response = await _apiClient.getProducts(page, 20); final products = response.products; // Cache voor 5 minuten _cache.set(cacheKey, products, ttl: const Duration(minutes: 5)); return Result.success(products); } on DioException catch (e) { // Fallback naar cache bij netwerkfout final cached = _cache.get(cacheKey); if (cached != null) { return Result.success(cached); } return Result.failure(_handleDioError(e)); } } } ``` ## 5. Netwerk Status Monitoring ```dart // lib/core/network/connectivity_service.dart import 'package:connectivity_plus/connectivity_plus.dart'; class ConnectivityService { final Connectivity _connectivity = Connectivity(); Stream<ConnectivityResult> get connectivityStream => _connectivity.onConnectivityChanged; Future<ConnectivityResult> checkConnectivity() async { return await _connectivity.checkConnectivity(); } Future<bool> hasInternetConnection() async { final result = await checkConnectivity(); return result != ConnectivityResult.none; } } ``` ## 6. State Management met Cubit/Bloc ```dart // lib/features/products/cubit/products_cubit.dart class ProductsCubit extends Cubit<ProductsState> { final ProductRepository _productRepository; final ConnectivityService _connectivityService; ProductsCubit(this._productRepository, this._connectivityService) : super(ProductsInitial()); Future<void> loadProducts({bool forceRefresh = false}) async { emit(ProductsLoading()); // Check connectivity final hasConnection = await _connectivityService.hasInternetConnection(); if (!hasConnection && forceRefresh) { emit(ProductsError(AppError.network())); return; } final result = await _productRepository.getProducts( forceRefresh: forceRefresh, ); result.when( success: (products) => emit(ProductsLoaded(products)), failure: (error) => emit(ProductsError(error)), ); } } ``` ## 7. Gebruikersinterface met Foutafhandeling ```dart // lib/features/products/widgets/product_list.dart class ProductList extends StatelessWidget { @override Widget build(BuildContext context) { return BlocConsumer<ProductsCubit, ProductsState>( listener: (context, state) { if (state is ProductsError) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(_getErrorMessage(state.error)), action: SnackBarAction( label: 'Opnieuw proberen', onPressed: () => context.read<ProductsCubit>().loadProducts(), ), ), ); } }, builder: (context, state) { if (state is ProductsLoading) { return const Center(child: CircularProgressIndicator()); } if (state is ProductsError) { return ErrorRetryWidget( error: state.error, onRetry: () => context.read<ProductsCubit>().loadProducts(), ); } if (state is ProductsLoaded) { return RefreshIndicator( onRefresh: () async { await context.read<ProductsCubit>().loadProducts(forceRefresh: true); }, child: ListView.builder( itemCount: state.products.length, itemBuilder: (context, index) => ProductItem(state.products[index]), ), ); } return const SizedBox.shrink(); }, ); } String _getErrorMessage(AppError error) { switch (error.type) { case AppErrorType.network: return 'Geen internetverbinding. Controleer uw netwerk.'; case AppErrorType.unauthorized: return 'Sessie verlopen. Log opnieuw in.'; case AppErrorType.server: return 'Serverfout. Probeer het later opnieuw.'; default: return 'Er is een fout opgetreden. Probeer het opnieuw.'; } } } ``` ## 8. Best Practices ### A. Dependency Injection ```dart // lib/core/di/service_locator.dart final getIt = GetIt.instance; void setupDependencies() { getIt.registerSingleton<Dio>(DioProvider.createDio()); getIt.registerSingleton<ApiClient>(ApiClient(getIt<Dio>())); getIt.registerSingleton<MemoryCache>(MemoryCache()); getIt.registerSingleton<ConnectivityService>(ConnectivityService()); getIt.registerFactory<AuthRepository>( () => AuthRepository(getIt<ApiClient>(), SharedPreferences.getInstance()), ); } ``` ### B. Environment Configuration ```dart // lib/core/config/environment.dart abstract class Environment { static const String baseUrl = String.fromEnvironment( 'BASE_URL', defaultValue: 'https://api.staging.jouwdomein.com/v1/', ); } ``` ### C. Model Classes ```dart // lib/features/auth/models/user.dart @JsonSerializable() class User { final String id; final String email; final String name; User({required this.id, required this.email, required this.name}); factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); Map<String, dynamic> toJson() => _$UserToJson(this); } ``` ## 9. Build Runner Commando Voer dit uit om de retrofit code te genereren: ```bash flutter pub run build_runner build ``` Deze architectuur biedt: - **Schone scheiding** van concerns - **Robuuste foutafhandeling** met fallback naar cache - **Efficiënte caching** strategie - **Netwerk status monitoring** - **Schaalbaar** voor meerdere API's - **Testbaar** door dependency injection Succes met je Flutter app ontwikkeling!
error: Generation failed [deepseek] prompt_id=4907 locale=en err=DeepSeek connection/timeout | Generation failed [deepseek] prompt_id=4907 locale=de err=DeepSeek connection/timeout