Skip to main content

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.
This guide covers advanced Flutter-specific configuration, development workflows, and platform-specific considerations for social.plus UIKit.

Development Environment Setup

Comprehensive Prerequisites

  • 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
  • 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
iOS Development:
  • macOS with Xcode Command Line Tools
  • iOS Simulator or physical device (iOS 12.0+)
  • CocoaPods installed and updated
Android Development:
  • 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

1

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>
2

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>
3

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,
    );
  }
}
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

Common Flutter Build Problems:
  1. Dependency conflicts:
    flutter clean
    flutter pub get
    flutter pub deps
    
  2. 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
    
  3. 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
    
  4. Dart analysis issues:
    dart analyze
    dart fix --apply
    
Memory and Performance Problems:
  1. 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);
    
  2. 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],
      ),
    );
    
  3. 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,
    );
    
iOS Specific Problems:
  1. 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);
    }
    
  2. iOS simulator issues:
    # Reset iOS simulator
    xcrun simctl erase all
    
    # Install on specific simulator
    flutter run -d "iPhone 14 Pro"
    
Android Specific Problems:
  1. Permission handling:
    Future<void> checkAndroidPermissions() async {
      if (await Permission.storage.isDenied) {
        await Permission.storage.request();
      }
      
      if (await Permission.camera.isDenied) {
        await Permission.camera.request();
      }
    }
    
  2. Gradle issues:
    # Clear Gradle cache
    cd android
    ./gradlew clean
    rm -rf .gradle
    cd ..
    flutter clean
    
Common Integration Problems:
  1. Initialization failures:
    // Add error handling to initialization
    try {
      await AmityUIKit.initialize(
        apiKey: apiKey,
        apiRegion: apiRegion,
      );
    } catch (error) {
      AmityErrorHandler.handleAmityError('initialization', error);
    }
    
  2. Configuration issues:
    // Validate configuration before use
    bool isValidConfig(Map<String, dynamic> config) {
      return config.containsKey('global_theme') &&
             config['global_theme'] != null;
    }
    
  3. 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