Dynamic Runtime Configuration with JSON and Provider
Nov 17, 2025



Summary
Summary
Summary
Summary
Learn how to implement dynamic runtime configuration in Flutter using JSON and Provider. Define a typed AppConfig, load baseline and remote JSON, merge and validate values, and expose changes via ChangeNotifier so widgets react to updates. Include caching, error handling, and testing to make runtime updates reliable for mobile development.
Learn how to implement dynamic runtime configuration in Flutter using JSON and Provider. Define a typed AppConfig, load baseline and remote JSON, merge and validate values, and expose changes via ChangeNotifier so widgets react to updates. Include caching, error handling, and testing to make runtime updates reliable for mobile development.
Learn how to implement dynamic runtime configuration in Flutter using JSON and Provider. Define a typed AppConfig, load baseline and remote JSON, merge and validate values, and expose changes via ChangeNotifier so widgets react to updates. Include caching, error handling, and testing to make runtime updates reliable for mobile development.
Learn how to implement dynamic runtime configuration in Flutter using JSON and Provider. Define a typed AppConfig, load baseline and remote JSON, merge and validate values, and expose changes via ChangeNotifier so widgets react to updates. Include caching, error handling, and testing to make runtime updates reliable for mobile development.
Key insights:
Key insights:
Key insights:
Key insights:
Configuration Model: Strongly typed models make JSON parsing predictable and simplify validation and defaults.
Loading And Parsing Configuration: Combine a baseline asset with remote overrides, validate, and cache last-good config to handle offline scenarios.
Using Provider For Configuration: Wrap config in a ChangeNotifier and provide it app-wide so widgets reactively rebuild on updates.
Applying Runtime Updates: Debounce and coordinate updates, use versioning for schema changes, and avoid mid-request endpoint swaps.
Testing And Validation: Unit-test parsing, widget-test UI reactions, and add integration tests to simulate remote changes and persistence.
Introduction
Dynamic runtime configuration is essential for modern Flutter mobile development. Instead of hardcoding flags, feature toggles, or UI values, store configuration as JSON and load it at runtime. This approach lets you change app behavior without redeploying, experiment with feature rollouts, and centralize environment settings. This tutorial shows a concise, practical pattern using a JSON configuration file and Provider (ChangeNotifier) to expose and update configuration across your app.
Configuration Model
Start with a simple, strongly typed model that maps to your JSON shape. Keep it minimal and serializable. A small model clarifies intent, makes validation easier, and keeps widgets decoupled from raw maps. Example fields: theme, feature flags, API endpoints, and intervals.
Use a factory constructor to parse JSON and small helpers for access. Favor nullable fields for optional keys and provide sane defaults where appropriate.
class AppConfig {
final bool newUiEnabled;
final String apiBase;
AppConfig({required this.newUiEnabled, required this.apiBase});
factory AppConfig.fromJson(Map<String, dynamic> json) => AppConfig(
newUiEnabled: json['newUiEnabled'] as bool? ?? false,
apiBase: json['apiBase'] as String? ?? 'https://api.example.com',
);
}Loading And Parsing Configuration
Decide whether config comes from local assets, remote endpoints, or both. Common pattern: ship a baseline JSON in assets and fetch an override JSON from a remote endpoint on startup. Merge the remote values over the baseline. Always validate and fallback to the baseline if parsing fails.
When fetching remotely, handle connectivity, timeouts, and stale caches. For mobile development, caching the last-good config in secure storage or shared preferences prevents broken launches when the network is unavailable.
Keep parsing synchronous after the JSON string is retrieved. Use try/catch to convert malformed JSON into a known state and log errors for diagnostics.
Using Provider For Configuration
Wrap your configuration in a ChangeNotifier and expose it with Provider or ChangeNotifierProvider. This grants reactive access across the widget tree and keeps UI code simple: read the provider in build methods to react to changes automatically.
Key responsibilities for the provider:
Hold the current AppConfig instance.
Expose a load method to fetch and apply JSON.
Merge remote overrides with the baseline.
Persist the last-known-good config.
Example provider snippet:
class ConfigNotifier extends ChangeNotifier {
AppConfig _config;
ConfigNotifier(this._config);
AppConfig get config => _config;
Future<void> updateFromJson(String json) async {
try {
final map = jsonDecode(json) as Map<String, dynamic>;
_config = AppConfig.fromJson(map);
notifyListeners();
} catch (_) {}
}
}In your app entry, provide ConfigNotifier at the top of the widget tree. Widgets can use context.watch().config to rebuild when values change.
Applying Runtime Updates
To update config at runtime, call updateFromJson after fetching new JSON. When notifyListeners runs, all listening widgets rebuild and adopt new behavior (feature toggles, endpoints, theme choices). For example, wrap theme selection in a builder that reads config.theme and applies a ThemeData change.
Coordinate updates carefully:
Avoid switching critical network endpoints mid-request. Apply endpoint changes during quiescent states or next restart if necessary.
Debounce frequent updates when polling remote config to prevent UI thrashing.
Provide a migration path if config shape changes; include a version key in JSON and write upgrade logic inside your provider.
Testing and Validation
Test both parsing and UI reactions. Unit-test AppConfig.fromJson with various payloads including missing fields. Widget tests can pump a provider with different configs and assert expected UI state. For mobile development, include integration tests that simulate remote config changes and verify persistence and fallback behavior.
Performance and Security Notes
Keep config JSON small — large payloads increase startup time. For sensitive values like API keys, avoid shipping them in plaintext; prefer server-managed tokens and short-lived credentials. Use HTTPS and validate TLS for remote fetches.
Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.
Conclusion
Using JSON-backed runtime configuration with Provider gives Flutter apps flexible, observable, and testable behavior control. Define a clear model, safely load and merge configs, expose changes through a ChangeNotifier, and design updates that respect network reliability and app stability. This pattern improves iteration speed in mobile development and enables safer feature rollouts without frequent app releases.
Introduction
Dynamic runtime configuration is essential for modern Flutter mobile development. Instead of hardcoding flags, feature toggles, or UI values, store configuration as JSON and load it at runtime. This approach lets you change app behavior without redeploying, experiment with feature rollouts, and centralize environment settings. This tutorial shows a concise, practical pattern using a JSON configuration file and Provider (ChangeNotifier) to expose and update configuration across your app.
Configuration Model
Start with a simple, strongly typed model that maps to your JSON shape. Keep it minimal and serializable. A small model clarifies intent, makes validation easier, and keeps widgets decoupled from raw maps. Example fields: theme, feature flags, API endpoints, and intervals.
Use a factory constructor to parse JSON and small helpers for access. Favor nullable fields for optional keys and provide sane defaults where appropriate.
class AppConfig {
final bool newUiEnabled;
final String apiBase;
AppConfig({required this.newUiEnabled, required this.apiBase});
factory AppConfig.fromJson(Map<String, dynamic> json) => AppConfig(
newUiEnabled: json['newUiEnabled'] as bool? ?? false,
apiBase: json['apiBase'] as String? ?? 'https://api.example.com',
);
}Loading And Parsing Configuration
Decide whether config comes from local assets, remote endpoints, or both. Common pattern: ship a baseline JSON in assets and fetch an override JSON from a remote endpoint on startup. Merge the remote values over the baseline. Always validate and fallback to the baseline if parsing fails.
When fetching remotely, handle connectivity, timeouts, and stale caches. For mobile development, caching the last-good config in secure storage or shared preferences prevents broken launches when the network is unavailable.
Keep parsing synchronous after the JSON string is retrieved. Use try/catch to convert malformed JSON into a known state and log errors for diagnostics.
Using Provider For Configuration
Wrap your configuration in a ChangeNotifier and expose it with Provider or ChangeNotifierProvider. This grants reactive access across the widget tree and keeps UI code simple: read the provider in build methods to react to changes automatically.
Key responsibilities for the provider:
Hold the current AppConfig instance.
Expose a load method to fetch and apply JSON.
Merge remote overrides with the baseline.
Persist the last-known-good config.
Example provider snippet:
class ConfigNotifier extends ChangeNotifier {
AppConfig _config;
ConfigNotifier(this._config);
AppConfig get config => _config;
Future<void> updateFromJson(String json) async {
try {
final map = jsonDecode(json) as Map<String, dynamic>;
_config = AppConfig.fromJson(map);
notifyListeners();
} catch (_) {}
}
}In your app entry, provide ConfigNotifier at the top of the widget tree. Widgets can use context.watch().config to rebuild when values change.
Applying Runtime Updates
To update config at runtime, call updateFromJson after fetching new JSON. When notifyListeners runs, all listening widgets rebuild and adopt new behavior (feature toggles, endpoints, theme choices). For example, wrap theme selection in a builder that reads config.theme and applies a ThemeData change.
Coordinate updates carefully:
Avoid switching critical network endpoints mid-request. Apply endpoint changes during quiescent states or next restart if necessary.
Debounce frequent updates when polling remote config to prevent UI thrashing.
Provide a migration path if config shape changes; include a version key in JSON and write upgrade logic inside your provider.
Testing and Validation
Test both parsing and UI reactions. Unit-test AppConfig.fromJson with various payloads including missing fields. Widget tests can pump a provider with different configs and assert expected UI state. For mobile development, include integration tests that simulate remote config changes and verify persistence and fallback behavior.
Performance and Security Notes
Keep config JSON small — large payloads increase startup time. For sensitive values like API keys, avoid shipping them in plaintext; prefer server-managed tokens and short-lived credentials. Use HTTPS and validate TLS for remote fetches.
Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.
Conclusion
Using JSON-backed runtime configuration with Provider gives Flutter apps flexible, observable, and testable behavior control. Define a clear model, safely load and merge configs, expose changes through a ChangeNotifier, and design updates that respect network reliability and app stability. This pattern improves iteration speed in mobile development and enables safer feature rollouts without frequent app releases.
Build Flutter Apps Faster with Vibe Studio
Build Flutter Apps Faster with Vibe Studio
Build Flutter Apps Faster with Vibe Studio
Build Flutter Apps Faster with Vibe Studio
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.






















