Scaling to a Real Project Structure =================================== While placing everything in a single file is great for learning, real-world applications require a structured approach. Organizing your files properly ensures that your project remains maintainable as it grows. In this section, we will refactor the single-file example into a standard directory structure. .. important:: **Import Consistency:** In Dart, always use **absolute package imports** (e.g., **package:your_project/ui/app_state.dart**). Mixing relative and absolute imports can cause Dart to treat the same file as two different instances, which will break your application state. A safe way to do this is to use: - Simple Lint: https://pub.dev/packages/simple_lint ---- Recommended Directory Structure ------------------------------- .. code-block:: text lib/ ├── ui/ │ ├── app_state.dart # All StateSlots are defined here │ └── pages/ │ └── counter_page.dart # UI Widgets └── main.dart # Entry point ---- Step 1: Define Application State (**lib/ui/app_state.dart**) ------------------------------------------------------------ First, move all your state logic into a dedicated file. This file becomes the "Source of Truth" for your entire app. .. code-block:: dart import 'package:simple_app_state/simple_app_state.dart'; final appState = SimpleAppState(); final countSlot = appState.slot('count', initial: 0); final logsSlot = appState.slot>( 'logs', initial: [], caster: (raw) => (raw as List).cast(), ); .. tip:: **The Golden Rule:** **app_state.dart** should **never** import any files from the UI layer (like widgets or pages). This ensures a clean data flow. ---- Step 2: Create the UI Layer (**lib/ui/pages/counter_page.dart**) ------------------------------------------------------------------ Next, move your widgets to the **pages/** directory. Use the **package import** to access the state. .. code-block:: dart import 'package:flutter/material.dart'; import 'package:simple_app_state/simple_app_state.dart'; // Use absolute package import import 'package:simple_app_state_demo/ui/app_state.dart'; class CounterPage extends SlotStatefulWidget { const CounterPage({super.key}); @override List get slots => [countSlot, logsSlot]; @override SlotState createState() => _CounterPageState(); } class _CounterPageState extends SlotState { @override Widget build(BuildContext context) { final count = countSlot.get(); final logs = logsSlot.get(); return Scaffold( appBar: AppBar(title: const Text('SimpleAppState Example')), body: Column( children: [ const SizedBox(height: 32), Text( 'Count: $count', style: Theme.of(context).textTheme.headlineMedium, ), const SizedBox(height: 24), ElevatedButton( onPressed: () { appState.batch(() { countSlot.update((v) => v + 1); logsSlot.update((oldCopy) { oldCopy.add('Increment at ${DateTime.now()}'); return oldCopy; }); }); }, child: const Text('Increment (batched)'), ), const Divider(height: 32), Expanded( child: ListView.builder( itemCount: logs.length, itemBuilder: (context, index) { return ListTile(title: Text(logs[index])); }, ), ), ], ), ); } } ---- Step 3: Cleanup the Entry Point (**lib/main.dart**) --------------------------------------------------- Finally, **main.dart** becomes very slim. Notice that all imports are absolute. .. code-block:: dart import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:simple_app_state_demo/ui/app_state.dart'; import 'package:simple_app_state_demo/ui/pages/counter_page.dart'; void main() { if (kDebugMode) { appState.setDebugListener((slot, oldV, newV) { debugPrint("Changed Slot:${slot.name}, to:$newV"); }); } runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp(home: CounterPage()); } } ---- Why This Structure? ------------------- 1. **Prevent Instance Duplication:** By using absolute package imports everywhere, you ensure that **appState** remains a single, consistent instance throughout the app. 2. **Discoverability:** New team members always know where to look for data: **ui/app_state.dart**. 3. **Predictable Dependencies:** The dependency always flows from **UI → State**. 4. **Testability:** You can write unit tests for your logic in **app_state.dart** without needing to build widgets. ---- Next step --------- Now that your project is well-organized, let’s look at some **common mistakes** to avoid when using SimpleAppState. Go to :doc:`common_mistakes` section.