Documentation Index
Fetch the complete documentation index at: https://learn.social.plus/llms.txt
Use this file to discover all available pages before exploring further.
Flutter UIKit is currently in Beta. Features and APIs may change in future releases.
Development Environment Setup
Comprehensive Prerequisites
Flutter SDK Requirements
Flutter SDK Requirements
- Flutter SDK: 3.0.0+ (stable channel recommended)
- Dart SDK: 2.17.6+ (< 3.0.0 for compatibility)
- Flutter Doctor: All checks should pass
- Hot Reload: Enabled for development efficiency
Development Tools
Development Tools
- Android Studio: 2022.2.1+ with Flutter plugin
- VS Code: Latest with Flutter and Dart extensions
- Xcode: 14+ (for iOS development)
- Git: Latest version for source control
Platform Requirements
Platform Requirements
iOS Development:
- macOS with Xcode Command Line Tools
- iOS Simulator or physical device (iOS 12.0+)
- CocoaPods installed and updated
- Android SDK 21+ (Android 7.0)
- Android Virtual Device (AVD) or physical device
- Java Development Kit (JDK) 11+
Environment Validation
Run these commands to ensure your environment is properly configured:# Check Flutter installation
flutter doctor -v
# Verify device connectivity
flutter devices
# Check for any issues
flutter doctor --android-licenses
# Update Flutter if needed
flutter upgrade
Advanced Configuration
Platform-Specific Configuration
iOS Advanced Setup
Update
ios/Runner/Info.plist with comprehensive permissions:<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Existing keys -->
<!-- Camera and Media Permissions -->
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to capture photos and videos for posts and stories.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access to record audio for video posts and voice messages.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs photo library access to select and share photos and videos.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app needs permission to save photos and videos to your photo library.</string>
<!-- Location (if using location features) -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app uses location to tag posts and find nearby communities.</string>
<!-- File Access -->
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>UIFileSharingEnabled</key>
<true/>
<!-- Background Modes -->
<key>UIBackgroundModes</key>
<array>
<string>background-fetch</string>
<string>background-processing</string>
</array>
<!-- Network Security -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSExceptionDomains</key>
<dict>
<key>api.amity.co</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<false/>
<key>NSExceptionMinimumTLSVersion</key>
<string>TLSv1.2</string>
</dict>
</dict>
</dict>
</dict>
</plist>
Android Advanced Configuration
Update
android/app/src/main/AndroidManifest.xml:<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- Permissions -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.VIBRATE" />
<!-- Optional: Location permissions -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- Hardware features -->
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.microphone" android:required="false" />
<application
android:label="Your App Name"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="false"
android:requestLegacyExternalStorage="false"
android:allowBackup="false"
tools:replace="android:allowBackup">
<!-- Your activities -->
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Intent filters -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- File provider for sharing files -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>
Build Configuration
Update
android/app/build.gradle for optimal performance:android {
namespace 'com.yourcompany.yourapp'
compileSdkVersion 34
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = '11'
}
defaultConfig {
applicationId "com.yourcompany.yourapp"
minSdkVersion 21
targetSdkVersion 34
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
multiDexEnabled true
// Performance optimizations
ndk {
abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86_64'
}
}
buildTypes {
release {
signingConfig signingConfigs.debug
minifyEnabled true
useProguard true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// Optimization flags
shrinkResources true
zipAlignEnabled true
}
debug {
signingConfig signingConfigs.debug
applicationIdSuffix ".debug"
debuggable true
}
}
// Packaging options
packagingOptions {
pickFirst '**/libc++_shared.so'
}
}
Advanced Component Usage
Custom Configuration Provider
import 'package:flutter/material.dart';
import 'package:amity_uikit_beta_service/amity_uikit_beta_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
class AmityConfigManager {
static AmityConfigManager? _instance;
static AmityConfigManager get instance => _instance ??= AmityConfigManager._();
AmityConfigManager._();
Map<String, dynamic>? _cachedConfig;
Future<Map<String, dynamic>> getConfiguration() async {
if (_cachedConfig != null) return _cachedConfig!;
final prefs = await SharedPreferences.getInstance();
final savedTheme = prefs.getString('theme_preference') ?? 'auto';
_cachedConfig = {
"global_theme": {
"light_theme": {
"primary_color": "#1054DE",
"secondary_color": "#292B32",
"base_color": "#000000",
"base_shade1_color": "#636878",
"base_shade2_color": "#898e9e",
"base_shade3_color": "#a5a9b5",
"base_shade4_color": "#ebecef",
"alert_color": "#FA4D30",
"background_color": "#FFFFFF",
"background_shade1_color": "#f6f7f8"
},
"dark_theme": {
"primary_color": "#1054DE",
"secondary_color": "#292B32",
"base_color": "#FFFFFF",
"base_shade1_color": "#a5a9b5",
"base_shade2_color": "#6e7487",
"base_shade3_color": "#40434e",
"base_shade4_color": "#292b32",
"alert_color": "#FA4D30",
"background_color": "#191919",
"background_shade1_color": "#40434e"
}
},
"preferred_theme": savedTheme,
"performance": {
"enableImageCaching": true,
"maxCacheSize": 100, // MB
"enablePreloading": true,
"maxConcurrentRequests": 5
},
"features": {
"enableStories": true,
"enableLiveStreaming": true,
"enablePolls": true,
"enableReactions": true
}
};
return _cachedConfig!;
}
Future<void> updateTheme(String theme) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('theme_preference', theme);
_cachedConfig?['preferred_theme'] = theme;
}
void clearCache() {
_cachedConfig = null;
}
}
class AdvancedAmityApp extends StatefulWidget {
final String apiKey;
final String apiRegion;
final String userId;
final String displayName;
final Widget child;
const AdvancedAmityApp({
Key? key,
required this.apiKey,
required this.apiRegion,
required this.userId,
required this.displayName,
required this.child,
}) : super(key: key);
@override
_AdvancedAmityAppState createState() => _AdvancedAmityAppState();
}
class _AdvancedAmityAppState extends State<AdvancedAmityApp> with WidgetsBindingObserver {
Map<String, dynamic>? config;
bool isLoading = true;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_loadConfiguration();
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.resumed:
// App resumed - refresh if needed
_refreshConfiguration();
break;
case AppLifecycleState.paused:
// App paused - save state if needed
break;
default:
break;
}
}
Future<void> _loadConfiguration() async {
try {
final loadedConfig = await AmityConfigManager.instance.getConfiguration();
setState(() {
config = loadedConfig;
isLoading = false;
});
} catch (error) {
debugPrint('Failed to load configuration: $error');
setState(() => isLoading = false);
}
}
Future<void> _refreshConfiguration() async {
AmityConfigManager.instance.clearCache();
await _loadConfiguration();
}
@override
Widget build(BuildContext context) {
if (isLoading) {
return MaterialApp(
home: Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
),
);
}
return AmityApp(
apiKey: widget.apiKey,
apiRegion: widget.apiRegion,
userId: widget.userId,
displayName: widget.displayName,
configs: config,
child: widget.child,
);
}
}
Navigation Integration with Flutter
import 'package:flutter/material.dart';
import 'package:amity_uikit_beta_service/amity_uikit_beta_service.dart';
class MainNavigationApp extends StatefulWidget {
@override
_MainNavigationAppState createState() => _MainNavigationAppState();
}
class _MainNavigationAppState extends State<MainNavigationApp> {
int _currentIndex = 0;
final PageController _pageController = PageController();
final List<Widget> _pages = [
AmitySocialHomePage(),
AmityChatHomePage(),
AmityStoriesPage(),
ProfilePage(),
];
final List<BottomNavigationBarItem> _navItems = [
BottomNavigationBarItem(
icon: Icon(Icons.home),
activeIcon: Icon(Icons.home, color: Color(0xFF1054DE)),
label: 'Feed',
),
BottomNavigationBarItem(
icon: Icon(Icons.chat_bubble_outline),
activeIcon: Icon(Icons.chat_bubble, color: Color(0xFF1054DE)),
label: 'Chat',
),
BottomNavigationBarItem(
icon: Icon(Icons.play_circle_outline),
activeIcon: Icon(Icons.play_circle_filled, color: Color(0xFF1054DE)),
label: 'Stories',
),
BottomNavigationBarItem(
icon: Icon(Icons.person_outline),
activeIcon: Icon(Icons.person, color: Color(0xFF1054DE)),
label: 'Profile',
),
];
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
void _onTabTapped(int index) {
setState(() => _currentIndex = index);
_pageController.animateToPage(
index,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
controller: _pageController,
onPageChanged: (index) => setState(() => _currentIndex = index),
children: _pages,
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
onTap: _onTabTapped,
items: _navItems,
selectedItemColor: Color(0xFF1054DE),
unselectedItemColor: Colors.grey,
backgroundColor: Colors.white,
elevation: 8,
),
);
}
}
class ProfilePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Profile'),
backgroundColor: Color(0xFF1054DE),
foregroundColor: Colors.white,
actions: [
IconButton(
icon: Icon(Icons.settings),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SettingsPage()),
);
},
),
],
),
body: AmityUserProfilePage(userId: 'current_user_id'),
);
}
}
class SettingsPage extends StatefulWidget {
@override
_SettingsPageState createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
String _selectedTheme = 'auto';
@override
void initState() {
super.initState();
_loadThemePreference();
}
Future<void> _loadThemePreference() async {
final prefs = await SharedPreferences.getInstance();
setState(() {
_selectedTheme = prefs.getString('theme_preference') ?? 'auto';
});
}
Future<void> _updateTheme(String theme) async {
await AmityConfigManager.instance.updateTheme(theme);
setState(() => _selectedTheme = theme);
// Trigger app rebuild with new theme
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Theme updated to $theme')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Settings'),
backgroundColor: Color(0xFF1054DE),
foregroundColor: Colors.white,
),
body: ListView(
children: [
ListTile(
title: Text('Theme'),
subtitle: Text('Choose your preferred theme'),
trailing: DropdownButton<String>(
value: _selectedTheme,
onChanged: (String? newValue) {
if (newValue != null) {
_updateTheme(newValue);
}
},
items: ['light', 'dark', 'auto'].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value.toUpperCase()),
);
}).toList(),
),
),
Divider(),
ListTile(
title: Text('Notifications'),
subtitle: Text('Manage notification preferences'),
trailing: Switch(
value: true, // Replace with actual preference
onChanged: (bool value) {
// Handle notification toggle
},
),
),
// Add more settings options as needed
],
),
);
}
}
Custom Theme Implementation
import 'package:flutter/material.dart';
class AmityThemeProvider extends InheritedWidget {
final Map<String, dynamic> themeConfig;
final VoidCallback onThemeChanged;
const AmityThemeProvider({
Key? key,
required this.themeConfig,
required this.onThemeChanged,
required Widget child,
}) : super(key: key, child: child);
static AmityThemeProvider? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<AmityThemeProvider>();
}
@override
bool updateShouldNotify(AmityThemeProvider oldWidget) {
return themeConfig != oldWidget.themeConfig;
}
Color get primaryColor => Color(int.parse(
themeConfig['global_theme']?[_getCurrentTheme()]?['primary_color']?.replaceFirst('#', '0xFF') ?? '0xFF1054DE'
));
Color get backgroundColor => Color(int.parse(
themeConfig['global_theme']?[_getCurrentTheme()]?['background_color']?.replaceFirst('#', '0xFF') ?? '0xFFFFFFFF'
));
Color get textColor => Color(int.parse(
themeConfig['global_theme']?[_getCurrentTheme()]?['base_color']?.replaceFirst('#', '0xFF') ?? '0xFF000000'
));
String _getCurrentTheme() {
final preferred = themeConfig['preferred_theme'] ?? 'light';
if (preferred == 'auto') {
return MediaQuery.of(context).platformBrightness == Brightness.dark ? 'dark_theme' : 'light_theme';
}
return preferred == 'dark' ? 'dark_theme' : 'light_theme';
}
}
class ThemedAmityButton extends StatelessWidget {
final String text;
final VoidCallback? onPressed;
final bool isPrimary;
const ThemedAmityButton({
Key? key,
required this.text,
this.onPressed,
this.isPrimary = true,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final themeProvider = AmityThemeProvider.of(context);
return ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: isPrimary ? themeProvider?.primaryColor : Colors.grey.shade200,
foregroundColor: isPrimary ? Colors.white : themeProvider?.textColor,
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text(
text,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
),
),
);
}
}
Performance Optimization
Memory Management
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
class AmityPerformanceManager {
static const _channel = MethodChannel('amity_performance');
static Timer? _memoryTimer;
static void startMonitoring() {
_memoryTimer = Timer.periodic(Duration(minutes: 1), (_) {
_checkMemoryUsage();
});
}
static void stopMonitoring() {
_memoryTimer?.cancel();
_memoryTimer = null;
}
static Future<void> _checkMemoryUsage() async {
if (kDebugMode) {
try {
final memoryInfo = await _channel.invokeMethod('getMemoryInfo');
final usedMemory = memoryInfo['usedMemory'] as int;
final maxMemory = memoryInfo['maxMemory'] as int;
final usagePercent = (usedMemory / maxMemory) * 100;
if (usagePercent > 80) {
debugPrint('High memory usage detected: ${usagePercent.toStringAsFixed(1)}%');
await _triggerGarbageCollection();
}
} catch (e) {
debugPrint('Failed to check memory usage: $e');
}
}
}
static Future<void> _triggerGarbageCollection() async {
await _channel.invokeMethod('triggerGC');
}
static Future<void> clearImageCache() async {
await _channel.invokeMethod('clearImageCache');
}
static Future<void> optimizeForLowMemory() async {
await _channel.invokeMethod('optimizeForLowMemory');
}
}
// Use in your app
class OptimizedAmityApp extends StatefulWidget {
@override
_OptimizedAmityAppState createState() => _OptimizedAmityAppState();
}
class _OptimizedAmityAppState extends State<OptimizedAmityApp> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
AmityPerformanceManager.startMonitoring();
}
@override
void dispose() {
AmityPerformanceManager.stopMonitoring();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didReceiveMemoryWarning() {
super.didReceiveMemoryWarning();
AmityPerformanceManager.clearImageCache();
AmityPerformanceManager.optimizeForLowMemory();
}
@override
Widget build(BuildContext context) {
return AdvancedAmityApp(
apiKey: 'YOUR_API_KEY',
apiRegion: 'YOUR_REGION',
userId: 'USER_ID',
displayName: 'DISPLAY_NAME',
child: MainNavigationApp(),
);
}
}
Network Optimization
import 'package:connectivity_plus/connectivity_plus.dart';
import 'dart:async';
class NetworkManager {
static final NetworkManager _instance = NetworkManager._internal();
factory NetworkManager() => _instance;
NetworkManager._internal();
ConnectivityResult? _currentConnectivity;
StreamSubscription<ConnectivityResult>? _connectivitySubscription;
final _connectivityController = StreamController<ConnectivityResult>.broadcast();
Stream<ConnectivityResult> get connectivityStream => _connectivityController.stream;
Future<void> initialize() async {
_currentConnectivity = await Connectivity().checkConnectivity();
_connectivitySubscription = Connectivity().onConnectivityChanged.listen((result) {
_currentConnectivity = result;
_connectivityController.add(result);
_adaptToNetworkConditions(result);
});
}
void dispose() {
_connectivitySubscription?.cancel();
_connectivityController.close();
}
void _adaptToNetworkConditions(ConnectivityResult connectivity) {
switch (connectivity) {
case ConnectivityResult.wifi:
_setHighQualityMode();
break;
case ConnectivityResult.mobile:
_setMediumQualityMode();
break;
case ConnectivityResult.none:
_setOfflineMode();
break;
default:
_setLowQualityMode();
}
}
void _setHighQualityMode() {
// Configure for high bandwidth
debugPrint('Network: High quality mode enabled');
}
void _setMediumQualityMode() {
// Configure for medium bandwidth
debugPrint('Network: Medium quality mode enabled');
}
void _setLowQualityMode() {
// Configure for low bandwidth
debugPrint('Network: Low quality mode enabled');
}
void _setOfflineMode() {
// Configure for offline operation
debugPrint('Network: Offline mode enabled');
}
bool get isConnected => _currentConnectivity != ConnectivityResult.none;
bool get isHighBandwidth => _currentConnectivity == ConnectivityResult.wifi;
}
// Network-aware widget
class NetworkAwareAmityWidget extends StatefulWidget {
final Widget child;
const NetworkAwareAmityWidget({Key? key, required this.child}) : super(key: key);
@override
_NetworkAwareAmityWidgetState createState() => _NetworkAwareAmityWidgetState();
}
class _NetworkAwareAmityWidgetState extends State<NetworkAwareAmityWidget> {
late StreamSubscription<ConnectivityResult> _networkSubscription;
ConnectivityResult _currentConnectivity = ConnectivityResult.wifi;
@override
void initState() {
super.initState();
_networkSubscription = NetworkManager().connectivityStream.listen((connectivity) {
setState(() => _currentConnectivity = connectivity);
});
}
@override
void dispose() {
_networkSubscription.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (_currentConnectivity == ConnectivityResult.none) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.wifi_off, size: 64, color: Colors.grey),
SizedBox(height: 16),
Text('No internet connection', style: TextStyle(fontSize: 18)),
SizedBox(height: 8),
Text('Please check your network settings', style: TextStyle(color: Colors.grey)),
],
),
),
);
}
return widget.child;
}
}
Error Handling and Debugging
Custom Error Handler
import 'dart:developer' as developer;
import 'package:flutter/foundation.dart';
class AmityErrorHandler {
static void initialize() {
FlutterError.onError = (FlutterErrorDetails details) {
FlutterError.presentError(details);
_logError(details.exception, details.stack, 'Flutter Error');
};
PlatformDispatcher.instance.onError = (error, stack) {
_logError(error, stack, 'Platform Error');
return true;
};
}
static void _logError(dynamic error, StackTrace? stack, String type) {
if (kDebugMode) {
developer.log(
'Error: $error',
name: 'AmityUIKit',
error: error,
stackTrace: stack,
);
} else {
// Send to crash reporting service in production
_sendToCrashlytics(error, stack, type);
}
}
static void _sendToCrashlytics(dynamic error, StackTrace? stack, String type) {
// Implementation for crash reporting
// e.g., Firebase Crashlytics
}
static void handleAmityError(String operation, dynamic error) {
debugPrint('Amity Operation Failed: $operation - $error');
// Handle specific Amity errors
if (error.toString().contains('401')) {
_handleAuthenticationError();
} else if (error.toString().contains('403')) {
_handlePermissionError();
} else if (error.toString().contains('network')) {
_handleNetworkError();
}
}
static void _handleAuthenticationError() {
debugPrint('Authentication error - redirecting to login');
}
static void _handlePermissionError() {
debugPrint('Permission error - user lacks necessary permissions');
}
static void _handleNetworkError() {
debugPrint('Network error - check connectivity');
}
}
// Error boundary widget
class AmityErrorBoundary extends StatefulWidget {
final Widget child;
final Widget? fallback;
const AmityErrorBoundary({
Key? key,
required this.child,
this.fallback,
}) : super(key: key);
@override
_AmityErrorBoundaryState createState() => _AmityErrorBoundaryState();
}
class _AmityErrorBoundaryState extends State<AmityErrorBoundary> {
Object? _error;
@override
Widget build(BuildContext context) {
if (_error != null) {
return widget.fallback ?? _buildErrorWidget();
}
return widget.child;
}
Widget _buildErrorWidget() {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline, size: 64, color: Colors.red),
SizedBox(height: 16),
Text('Something went wrong', style: TextStyle(fontSize: 18)),
SizedBox(height: 8),
Text('Please try restarting the app', style: TextStyle(color: Colors.grey)),
SizedBox(height: 16),
ElevatedButton(
onPressed: () => setState(() => _error = null),
child: Text('Retry'),
),
],
),
),
);
}
@override
void didUpdateWidget(AmityErrorBoundary oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.child != widget.child) {
_error = null;
}
}
}
Troubleshooting
Build and Compilation Issues
Build and Compilation Issues
Common Flutter Build Problems:
-
Dependency conflicts:
flutter clean flutter pub get flutter pub deps -
Version conflicts:
# In pubspec.yaml, specify exact versions dependencies: flutter: sdk: flutter amity_uikit_beta_service: ^1.0.0 dependency_overrides: # Override conflicting packages if needed some_package: ^2.0.0 -
Platform-specific build issues:
# iOS cd ios rm -rf Pods Podfile.lock pod install --repo-update cd .. # Android cd android ./gradlew clean cd .. flutter clean flutter pub get -
Dart analysis issues:
dart analyze dart fix --apply
Runtime and Performance Issues
Runtime and Performance Issues
Memory and Performance Problems:
-
Memory leaks:
// Always dispose controllers and subscriptions @override void dispose() { _controller.dispose(); _subscription.cancel(); super.dispose(); } // Use weak references for callbacks WeakReference<MyWidget> weakRef = WeakReference(this); -
Widget rebuilds:
// Use const constructors const MyWidget({Key? key}) : super(key: key); // Optimize with keys ListView.builder( itemBuilder: (context, index) => MyItem( key: ValueKey(items[index].id), item: items[index], ), ); -
Image loading optimization:
// Use cached network image CachedNetworkImage( imageUrl: imageUrl, placeholder: (context, url) => CircularProgressIndicator(), errorWidget: (context, url, error) => Icon(Icons.error), memCacheWidth: 300, memCacheHeight: 300, );
Platform-Specific Issues
Platform-Specific Issues
iOS Specific Problems:
-
Permission issues:
import 'package:permission_handler/permission_handler.dart'; Future<bool> requestPermissions() async { final permissions = [ Permission.camera, Permission.microphone, Permission.photos, ]; final statuses = await permissions.request(); return statuses.values.every((status) => status.isGranted); } -
iOS simulator issues:
# Reset iOS simulator xcrun simctl erase all # Install on specific simulator flutter run -d "iPhone 14 Pro"
-
Permission handling:
Future<void> checkAndroidPermissions() async { if (await Permission.storage.isDenied) { await Permission.storage.request(); } if (await Permission.camera.isDenied) { await Permission.camera.request(); } } -
Gradle issues:
# Clear Gradle cache cd android ./gradlew clean rm -rf .gradle cd .. flutter clean
UIKit Integration Issues
UIKit Integration Issues
Common Integration Problems:
-
Initialization failures:
// Add error handling to initialization try { await AmityUIKit.initialize( apiKey: apiKey, apiRegion: apiRegion, ); } catch (error) { AmityErrorHandler.handleAmityError('initialization', error); } -
Configuration issues:
// Validate configuration before use bool isValidConfig(Map<String, dynamic> config) { return config.containsKey('global_theme') && config['global_theme'] != null; } -
Network connectivity:
// Check connectivity before operations Future<bool> isNetworkAvailable() async { final connectivity = await Connectivity().checkConnectivity(); return connectivity != ConnectivityResult.none; }
Testing
Unit Testing
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:amity_uikit_beta_service/amity_uikit_beta_service.dart';
class MockAmityUIKit extends Mock implements AmityUIKit {}
void main() {
group('AmityUIKit Tests', () {
late MockAmityUIKit mockUIKit;
setUp(() {
mockUIKit = MockAmityUIKit();
});
testWidgets('AmityApp initializes correctly', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: AmityApp(
apiKey: 'test_key',
apiRegion: 'us',
userId: 'test_user',
displayName: 'Test User',
child: Container(),
),
),
);
expect(find.byType(AmityApp), findsOneWidget);
});
test('Configuration validation works', () {
final validConfig = {
'global_theme': {
'light_theme': {'primary_color': '#1054DE'}
}
};
expect(isValidConfig(validConfig), isTrue);
});
});
}
bool isValidConfig(Map<String, dynamic> config) {
return config.containsKey('global_theme') &&
config['global_theme'] != null;
}
Integration Testing
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:myapp/main.dart' as app;
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('Amity UIKit Integration Tests', () {
testWidgets('Social feed loads and displays posts', (WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
// Navigate to social feed
await tester.tap(find.text('Social'));
await tester.pumpAndSettle();
// Verify social feed is displayed
expect(find.byType(AmitySocialHomePage), findsOneWidget);
// Test post creation
await tester.tap(find.text('Create Post'));
await tester.pumpAndSettle();
await tester.enterText(find.byType(TextField), 'Test post content');
await tester.tap(find.text('Post'));
await tester.pumpAndSettle();
// Verify post appears in feed
expect(find.text('Test post content'), findsOneWidget);
});
testWidgets('Chat functionality works', (WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
// Navigate to chat
await tester.tap(find.text('Chat'));
await tester.pumpAndSettle();
// Verify chat interface is displayed
expect(find.byType(AmityChatHomePage), findsOneWidget);
});
});
}
Next Steps
Authentication Setup
Configure user authentication and session management
Customization Guide
Learn how to customize themes and components