flutter_settings_framework 0.6.0 copy "flutter_settings_framework: ^0.6.0" to clipboard
flutter_settings_framework: ^0.6.0 copied to clipboard

Declarative Flutter settings with bilingual search, a Riverpod adapter, and ready-made settings UI.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_settings_framework/flutter_settings_framework.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Create settings registry
  final registry = createExampleRegistry();

  // Initialize settings framework
  final settings = await initializeSettings(
    registry: registry,
    storage: SharedPreferencesStorage(),
  );

  runApp(
    ProviderScope(
      overrides: [
        settingsControllerProvider.overrideWithValue(settings.controller),
        settingsSearchIndexProvider.overrideWithValue(settings.searchIndex),
        settingsProvidersProvider.overrideWithValue(settings),
      ],
      child: const MyApp(),
    ),
  );
}

// =============================================================================
// SETTING DEFINITIONS
// =============================================================================

/// General settings section
const generalSection = SettingSection(
  key: 'general',
  titleKey: 'General',
  icon: Icons.settings,
  order: 0,
);

/// Appearance settings section
const appearanceSection = SettingSection(
  key: 'appearance',
  titleKey: 'Appearance',
  icon: Icons.palette,
  order: 1,
);

/// Theme mode setting
const themeModeSetting = EnumSetting(
  'theme_mode',
  defaultValue: 'system',
  titleKey: 'Theme Mode',
  options: ['system', 'light', 'dark'],
  section: 'general',
  searchTerms: {
    'en': ['theme', 'dark', 'light', 'mode', 'appearance'],
  },
);

/// Notifications setting
const notificationsSetting = BoolSetting(
  'notifications_enabled',
  defaultValue: true,
  titleKey: 'Enable Notifications',
  icon: Icons.notifications,
  section: 'general',
  searchTerms: {
    'en': ['notifications', 'alerts', 'notify'],
  },
);

/// Theme color setting
const themeColorSetting = ColorSetting(
  'theme_color',
  defaultValue: 0xFF6200EE,
  titleKey: 'Theme Color',
  section: 'appearance',
  searchTerms: {
    'en': ['color', 'theme', 'accent'],
  },
);

/// Card elevation setting
const cardElevationSetting = DoubleSetting(
  'card_elevation',
  defaultValue: 2.0,
  min: 0.0,
  max: 8.0,
  step: 0.5,
  titleKey: 'Card Elevation',
  section: 'appearance',
  searchTerms: {
    'en': ['elevation', 'shadow', 'card'],
  },
);

/// Font size scale setting
const fontSizeScaleSetting = EnumSetting(
  'font_size_scale',
  defaultValue: 'normal',
  titleKey: 'Font Size',
  options: ['small', 'normal', 'large', 'extra_large'],
  section: 'appearance',
  searchTerms: {
    'en': ['font', 'size', 'text', 'scale'],
  },
);

/// Create the settings registry
SettingsRegistry createExampleRegistry() {
  return SettingsRegistry.withSettings(
    sections: [generalSection, appearanceSection],
    settings: [
      themeModeSetting,
      notificationsSetting,
      themeColorSetting,
      cardElevationSetting,
      fontSizeScaleSetting,
    ],
  );
}

// =============================================================================
// APP
// =============================================================================

class MyApp extends ConsumerWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final themeModeStr = ref.watchSetting(themeModeSetting);
    final themeMode = _parseThemeMode(themeModeStr);

    return MaterialApp(
      title: 'Settings Framework Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      themeMode: themeMode,
      home: const SettingsExamplePage(),
    );
  }

  ThemeMode _parseThemeMode(String mode) {
    switch (mode) {
      case 'light':
        return ThemeMode.light;
      case 'dark':
        return ThemeMode.dark;
      case 'system':
      default:
        return ThemeMode.system;
    }
  }
}

// =============================================================================
// SETTINGS PAGE
// =============================================================================

class SettingsExamplePage extends ConsumerStatefulWidget {
  const SettingsExamplePage({super.key});

  @override
  ConsumerState<SettingsExamplePage> createState() =>
      _SettingsExamplePageState();
}

class _SettingsExamplePageState extends ConsumerState<SettingsExamplePage> {
  final TextEditingController _searchController = TextEditingController();
  List<SearchResult> _searchResults = [];
  bool _isSearching = false;
  @override
  void dispose() {
    _searchController.dispose();
    super.dispose();
  }

  void _performSearch(String query) {
    if (query.isEmpty) {
      setState(() {
        _searchResults = [];
        _isSearching = false;
      });
      return;
    }

    setState(() {
      _isSearching = true;
      _searchResults =
          ref.read(settingsProvidersProvider).searchIndex.search(query);
    });
  }

  @override
  Widget build(BuildContext context) {
    final settings = ref.watch(settingsProvidersProvider);

    return Scaffold(
      appBar: AppBar(
        title: const Text('Settings Framework Example'),
        actions: [
          IconButton(
            icon: const Icon(Icons.dashboard_customize),
            tooltip: 'Open RegistrySettingsPage',
            onPressed: () {
              Navigator.of(context).push(
                MaterialPageRoute<void>(
                  builder: (_) => RegistrySettingsPage(
                    registry: createExampleRegistry(),
                    settings: settings,
                    title: 'Settings (RegistrySettingsPage)',
                    searchHint: 'Search settings...',
                    sectionTitleBuilder: (key) => key,
                  ),
                ),
              );
            },
          ),
          IconButton(
            icon: Icon(_isSearching ? Icons.close : Icons.search),
            onPressed: () {
              setState(() {
                _isSearching = !_isSearching;
                if (!_isSearching) {
                  _searchController.clear();
                  _searchResults = [];
                }
              });
            },
          ),
        ],
      ),
      body: Column(
        children: [
          if (_isSearching)
            Padding(
              padding: const EdgeInsets.all(16),
              child: TextField(
                controller: _searchController,
                decoration: const InputDecoration(
                  hintText: 'Search settings...',
                  prefixIcon: Icon(Icons.search),
                  border: OutlineInputBorder(),
                ),
                onChanged: _performSearch,
                autofocus: true,
              ),
            ),
          Expanded(
            child: _isSearching && _searchResults.isNotEmpty
                ? _buildSearchResults()
                : _buildSettingsList(),
          ),
        ],
      ),
    );
  }

  Widget _buildSearchResults() {
    return ListView.builder(
      padding: const EdgeInsets.all(16),
      itemCount: _searchResults.length,
      itemBuilder: (context, index) {
        final result = _searchResults[index];
        return Card(
          margin: const EdgeInsets.only(bottom: 8),
          child: ListTile(
            title: Text(result.setting.titleKey),
            subtitle: Text(
                'Match: "${result.matchedTerm}" (score: ${result.score.toStringAsFixed(2)})'),
            trailing: _buildSettingValue(result.setting),
          ),
        );
      },
    );
  }

  Widget _buildSettingsList() {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        // General Section
        _buildSectionHeader('General'),
        const SizedBox(height: 8),
        _buildSettingTile(themeModeSetting),
        _buildSettingTile(notificationsSetting),
        const SizedBox(height: 24),

        // Appearance Section
        _buildSectionHeader('Appearance'),
        const SizedBox(height: 8),
        _buildSettingTile(themeColorSetting),
        _buildSettingTile(cardElevationSetting),
        _buildSettingTile(fontSizeScaleSetting),
        const SizedBox(height: 24),

        // Current Values Display
        _buildSectionHeader('Current Values'),
        const SizedBox(height: 8),
        _buildValueCard('Theme Mode', ref.watchSetting(themeModeSetting)),
        _buildValueCard(
            'Notifications', ref.watchSetting(notificationsSetting).toString()),
        _buildValueCard('Theme Color',
            '#${ref.watchSetting(themeColorSetting).toRadixString(16).toUpperCase()}'),
        _buildValueCard('Card Elevation',
            ref.watchSetting(cardElevationSetting).toString()),
        _buildValueCard('Font Size', ref.watchSetting(fontSizeScaleSetting)),
      ],
    );
  }

  Widget _buildSectionHeader(String title) {
    return Text(
      title,
      style: Theme.of(context).textTheme.titleLarge?.copyWith(
            fontWeight: FontWeight.bold,
          ),
    );
  }

  Widget _buildSettingTile(SettingDefinition setting) {
    return Card(
      margin: const EdgeInsets.only(bottom: 8),
      child: _buildSettingContent(setting),
    );
  }

  Widget _buildSettingContent(SettingDefinition setting) {
    if (setting is BoolSetting) {
      return SwitchSettingsTile.fromSetting(
        setting: setting,
        title: setting.titleKey,
        value: ref.watch(ref.settings.provider(setting)),
        onChanged: (value) {
          ref.read(ref.settings.provider(setting).notifier).set(value);
        },
      );
    } else if (setting is EnumSetting) {
      return SelectSettingsTile.fromEnumSetting(
        setting: setting,
        title: setting.titleKey,
        value: ref.watch(ref.settings.provider(setting)),
        labelBuilder: (opt) => opt.replaceAll('_', ' ').toUpperCase(),
        onChanged: (value) {
          if (value != null) {
            ref.read(ref.settings.provider(setting).notifier).set(value);
          }
        },
      );
    } else if (setting is ColorSetting) {
      return ColorSettingsTile.fromSetting(
        setting: setting,
        title: setting.titleKey,
        value: ref.watch(ref.settings.provider(setting)),
        onChanged: (value) {
          ref.read(ref.settings.provider(setting).notifier).set(value);
        },
      );
    } else if (setting is DoubleSetting) {
      return SliderSettingsTile.fromDoubleSetting(
        setting: setting,
        title: setting.titleKey,
        value: ref.watch(ref.settings.provider(setting)),
        onChanged: (value) {
          ref.read(ref.settings.provider(setting).notifier).set(value);
        },
      );
    } else {
      return ListTile(
        title: Text(setting.titleKey),
        subtitle: Text(_buildSettingValue(setting).toString()),
      );
    }
  }

  Widget _buildSettingValue(SettingDefinition setting) {
    if (setting is BoolSetting) {
      final value = ref.watch(ref.settings.provider(setting));
      return Text(value.toString());
    } else if (setting is EnumSetting) {
      final value = ref.watch(ref.settings.provider(setting));
      return Text(value);
    } else if (setting is ColorSetting) {
      final color = ref.watch(ref.settings.provider(setting));
      return Container(
        width: 24,
        height: 24,
        decoration: BoxDecoration(
          color: Color(color),
          shape: BoxShape.circle,
          border: Border.all(color: Colors.grey),
        ),
      );
    } else if (setting is DoubleSetting) {
      final value = ref.watch(ref.settings.provider(setting));
      return Text(value.toStringAsFixed(1));
    }
    return const SizedBox.shrink();
  }

  Widget _buildValueCard(String label, String value) {
    return Card(
      margin: const EdgeInsets.only(bottom: 8),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Text(
              label,
              style: Theme.of(context).textTheme.bodyLarge,
            ),
            Text(
              value,
              style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                    fontWeight: FontWeight.bold,
                  ),
            ),
          ],
        ),
      ),
    );
  }
}
0
likes
160
points
52
downloads

Documentation

API reference

Publisher

verified publishershenepoy.com

Weekly Downloads

Declarative Flutter settings with bilingual search, a Riverpod adapter, and ready-made settings UI.

Repository (GitHub)
View/report issues

License

MPL-2.0 (license)

Dependencies

easy_localization, flutter, flutter_riverpod, shared_preferences

More

Packages that depend on flutter_settings_framework