SYP (Simple Yet Powerful) State Manager
A lightweight, efficient state management solution for Flutter applications. SYP State Manager provides a simple way to manage application state with minimal boilerplate code, making it easy to share and update state across your widgets.
Features
- Simple API: Easy-to-use interface with minimal learning curve
- Efficient Updates: Only rebuilds widgets that are watching a particular state
- Global State Access: Access state from anywhere in your application
- Type-Safe: Fully typed state management for better developer experience
- No Context Required: Read states without BuildContext
- Performance Optimized: Minimizes unnecessary rebuilds
Getting started
Add the package to your pubspec.yaml:
dependencies:
syp_state_manager: ^1.0.0
Then run:
flutter pub get
Import the package in your Dart code:
import 'package:syp_state_manager/syp_state_manager.dart';
Usage
1. Create a State Model
First, create a state model by extending StateModel and using ReactiveStateMixin:
import 'package:syp_state_manager/state_manager.dart';
class CounterState extends StateModel with ReactiveStateMixin {
late final ReactiveProperty<int> _count;
CounterState() {
_count = reactive<int>(0, key: 'count');
}
int get count => _count.value;
set count(int value) {
_count.value = value;
}
void increment() {
count = count + 1;
}
void reset() {
_count.setSilent(0); // Update without notification
}
}
2. Using with StatefulWidget
Use the ConsumerState class to manage state in stateful widgets:
import 'package:flutter/material.dart';
import 'package:syp_state_manager/consumer_state.dart';
class CounterPage extends StatefulWidget {
const CounterPage({Key? key}) : super(key: key);
@override
State<CounterPage> createState() => _CounterPageState();
}
class _CounterPageState extends ConsumerState<CounterPage> {
@override
Widget build(BuildContext context) {
// Watch the counter state - this will rebuild when state changes
final counterState = ref.watch<CounterState>(() => CounterState());
return Scaffold(
appBar: AppBar(title: const Text('Counter Example')),
body: Center(
child: Text(
'Count: ${counterState.count}',
style: const TextStyle(fontSize: 24),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
counterState.increment(); // Update state
},
child: const Icon(Icons.add),
),
);
}
@override
void dispose() {
ref.dispose<CounterState>(); // Clean up state references
super.dispose();
}
}
3. Using with ConsumerWidget (Stateless approach)
For a more streamlined approach, use the ConsumerWidget class:
import 'package:flutter/material.dart';
import 'package:syp_state_manager/consumer_widget.dart';
import 'package:syp_state_manager/state_ref.dart';
class CounterView extends ConsumerWidget {
const CounterView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, StateRef ref) {
// Watch the counter state
final counterState = ref.watch<CounterState>(() => CounterState());
return Scaffold(
appBar: AppBar(title: const Text('Consumer Widget Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Count: ${counterState.count}',
style: const TextStyle(fontSize: 24),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
counterState.increment();
},
child: const Text('Increment'),
),
],
),
),
);
}
}
4. Reading State Without Watching
You can also read state without subscribing to updates:
import 'package:flutter/material.dart';
import 'package:syp_state_manager/consumer_widget.dart';
import 'package:syp_state_manager/state_ref.dart';
class ReadOnlyCounterButton extends ConsumerWidget {
const ReadOnlyCounterButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, StateRef ref) {
// This widget won't rebuild when CounterState changes
return ElevatedButton(
onPressed: () {
// Read state without watching
final counterState = ref.read<CounterState>();
if (counterState != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Current count: ${counterState.count}')),
);
}
},
child: const Text('Show Current Count'),
);
}
}
Advanced Usage
Multiple Reactive Properties
You can define multiple reactive properties in a single state model:
import 'package:syp_state_manager/state_manager.dart';
class BottomNavigationState extends StateModel with ReactiveStateMixin {
late final ReactiveProperty<int> _notificationUnreadCount;
late final ReactiveProperty<int> _pendingDataRequestCount;
BottomNavigationState() {
_notificationUnreadCount = reactive<int>(0, key: 'notificationUnreadCount');
_pendingDataRequestCount = reactive<int>(0, key: 'pendingDataRequestCount');
}
int get notificationUnreadCount => _notificationUnreadCount.value;
set notificationUnreadCount(int count) {
_notificationUnreadCount.value = count;
}
int get pendingDataRequestCount => _pendingDataRequestCount.value;
set pendingDataRequestCount(int count) {
_pendingDataRequestCount.value = count;
}
void reset() {
_notificationUnreadCount.setSilent(0);
_pendingDataRequestCount.setSilent(0);
}
}
Usage example:
import 'package:flutter/material.dart';
import 'package:syp_state_manager/consumer_widget.dart';
import 'package:syp_state_manager/state_ref.dart';
class NavigationBar extends ConsumerWidget {
const NavigationBar({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, StateRef ref) {
final navState = ref.watch<BottomNavigationState>(() => BottomNavigationState());
return BottomNavigationBar(
items: [
const BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Badge(
label: Text('${navState.notificationUnreadCount}'),
isLabelVisible: navState.notificationUnreadCount > 0,
child: const Icon(Icons.notifications),
),
label: 'Notifications',
),
BottomNavigationBarItem(
icon: Badge(
label: Text('${navState.pendingDataRequestCount}'),
isLabelVisible: navState.pendingDataRequestCount > 0,
child: const Icon(Icons.sync),
),
label: 'Sync',
),
],
onTap: (index) {
if (index == 1) {
// Reset notification count when navigating to notifications
navState.notificationUnreadCount = 0;
} else if (index == 2) {
// Reset pending requests when navigating to sync
navState.pendingDataRequestCount = 0;
}
},
);
}
}
Creating a Provider Widget
You can create a provider widget to initialize state at the app level:
import 'package:flutter/material.dart';
import 'package:syp_state_manager/consumer_widget.dart';
import 'package:syp_state_manager/state_ref.dart';
class AppStateProvider extends ConsumerWidget {
final Widget child;
const AppStateProvider({
Key? key,
required this.child,
}) : super(key: key);
@override
Widget build(BuildContext context, StateRef ref) {
// Initialize your global states
ref.watch<CounterState>(() => CounterState());
// Initialize other states as needed
return child;
}
}
// Use it in your app
void main() {
runApp(
AppStateProvider(
child: MyApp(),
),
);
}
Combining Multiple States
Working with multiple states is straightforward:
import 'package:flutter/material.dart';
import 'package:syp_state_manager/consumer_widget.dart';
import 'package:syp_state_manager/state_ref.dart';
import 'package:syp_state_manager/state_manager.dart';
class ThemeState extends StateModel with ReactiveStateMixin {
late final ReactiveProperty<bool> _isDarkMode;
ThemeState() {
_isDarkMode = reactive<bool>(false, key: 'isDarkMode');
}
bool get isDarkMode => _isDarkMode.value;
set isDarkMode(bool value) {
_isDarkMode.value = value;
}
void toggleTheme() {
isDarkMode = !isDarkMode;
}
}
class SettingsPage extends ConsumerWidget {
const SettingsPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, StateRef ref) {
final counterState = ref.watch<CounterState>(() => CounterState());
final themeState = ref.watch<ThemeState>(() => ThemeState());
return Scaffold(
appBar: AppBar(title: const Text('Settings')),
body: Column(
children: [
SwitchListTile(
title: const Text('Dark Mode'),
value: themeState.isDarkMode,
onChanged: (_) => themeState.toggleTheme(),
),
ListTile(
title: Text('Counter Value: ${counterState.count}'),
trailing: IconButton(
icon: const Icon(Icons.add),
onPressed: () => counterState.increment(),
),
),
],
),
);
}
}
Additional information
When to use
SYP State Manager is ideal for:
- Small to medium-sized applications
- Applications where you need simple state management without complex architecture
- Projects where you want to avoid the boilerplate of larger state management solutions
Contribution
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.