Signals - wurzelsand/flutter-memos GitHub Wiki
import 'package:flutter/material.dart';
import 'package:signals_flutter/signals_flutter.dart';
final counter = signal(0);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Signals with Global Signal')),
body: Center(
child: SignalBuilder(builder: (context) => Text('Value: $counter')),
),
floatingActionButton: FloatingActionButton(
onPressed: () => counter.value++,
child: Icon(Icons.add),
),
),
);
}
}import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:signals_flutter/signals_flutter.dart';
// --- DATA LAYER ---
class WeatherRepository {
Future<String> fetchWeatherFromServer(String city) async {
await Future.delayed(const Duration(seconds: 1));
return '21°C, leicht bewölkt in $city';
}
}
// --- LOGIC LAYER ---
class WeatherController {
final WeatherRepository _repository;
WeatherController(this._repository);
final weatherData = signal<String?>(null);
final isLoading = signal<bool>(false);
Future<void> loadWeather(String city) async {
isLoading.value = true;
try {
final result = await _repository.fetchWeatherFromServer(city);
weatherData.value = result;
} catch (e) {
weatherData.value = 'Fehler beim Laden';
} finally {
isLoading.value = false;
}
}
}
// --- DEPENDENCY INJECTION ---
final di = GetIt.instance;
void setupArchitecture() {
di.registerLazySingleton(() => WeatherRepository());
di.registerLazySingleton(() => WeatherController(di<WeatherRepository>()));
}
// --- MAIN START ---
void main() {
// Zwingt die Flutter-Engine zur korrekten Initialisierung vor dem Isolat-Start
WidgetsFlutterBinding.ensureInitialized();
// 1. Diese Zeile schaltet die automatischen Konsolen-Logs von Signals ab:
// SignalsObserver.instance = null;
// 2. (Optional) Wenn du auch das Tracking für die Flutter DevTools deaktivieren willst:
// signalsDevToolsEnabled = false;
setupArchitecture();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: WeatherScreen());
}
}
// --- PRESENTATION LAYER (UI) ---
class WeatherScreen extends StatelessWidget {
const WeatherScreen({super.key});
@override
Widget build(BuildContext context) {
final controller = di<WeatherController>();
return Scaffold(
appBar: AppBar(title: const Text('Wetter App (Signals & GetIt)')),
body: Center(
// Hier ist der korrekte SignalBuilder
child: SignalBuilder(
builder: (context) {
if (controller.isLoading.value) {
return const CircularProgressIndicator();
}
return Text(
controller.weatherData.value ?? 'Noch keine Daten geladen.',
style: const TextStyle(fontSize: 20),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => controller.loadWeather('Dresden'),
child: const Icon(Icons.refresh),
),
);
}
}signals_instead_riverpod_providers.dart
Beim Einsatz von GetIt (als Service-Locator) in Kombination mit Signals (für reaktive Zustände) kann die Architektur einer App bei unbedachtem Vorgehen schnell unübersichtlich werden. Die folgenden fünf Regeln dienen als Leitplanken, um deinen Code sauber, performant und frei von Memory Leaks zu halten.
-
Nutze
.readonly()für den Export
Mach alle Signals in deinen GetIt-Servicesprivateund biete der UI nur einReadonlySignalan. Das zwingt dich und dein Team dazu, die verändernde Logik direkt im Service zu kapseln, anstatt sie über die gesamte App zu verstreuen. -
Widgets lösen nur Aktionen aus
Ein Widget sollte niemals Business-Logik berechnen oder Werte direkt in GetIt-Instanzen überschreiben. Das Widget agiert rein als Konsument und Signalgeber; es sagt dem Service lediglich über explizite Methoden, was zu tun ist (z. B.userService.login(...)odercartService.addItem(...)). -
Vermeide, dass Signals andere Signals außerhalb ihres Bereichs mutieren
Eineffectin Service A sollte nicht das Signal in Service B direkt verändern. Wenn Service B von den Daten aus Service A abhängt, nutze dafür lieber eincomputedSignal, welches die Abhängigkeit deklarativ und automatisch auflöst. -
Achte auf den Lifecycle bei kurzlebigen Instanzen
Wenn ducomputedSignals odereffects in temporären Factory- oder Scoped-Instanzen (oder direkt in Widgets) erstellst, die von globalen GetIt-Singletons abhängen, hält das Singleton eine Referenz auf diese Instanz. Du musst sie beim Schließen zwingend via.dispose()aufräumen, um Memory Leaks zu verhindern. -
Bündele zusammenhängende Updates mit
batch()
Wenn eine einzige Benutzeraktion mehrere Signals gleichzeitig verändert – selbst wenn diese über verschiedene GetIt-Services verteilt sind –, wickle die Aufrufe in einbatch()ein. Das verhindert unruhige Zwischenzustände (Glitches) sowie doppelte, ressourcenfressende Berechnungen in nachgelagerten Elementen und deiner UI.