Appearance
Kapitel 19: Erweiterte Lernswege
19.1 Erweiterte Zustandsverwaltung (Bloc, GetX, Riverpod)
Bloc (Business Logic Component)
Wann verwenden?
- ✅ Große, komplexe Projekte
- ✅ Strenge Trennung von Logik und UI
- ✅ Gute Testbarkeit
- ✅ Enterprise-Anwendungen
Grundlegende Struktur:
Bloc besteht aus:
1. Events (Ereignisse) - Benutzeraktionen
2. States (Zustände) - UI-Zustände
3. Bloc - Verarbeitet Events zu StatesEinfaches Beispiel:
dart
// Event
abstract class CounterEvent {}
class IncrementEvent extends CounterEvent {}
// State
class CounterState {
final int counter;
CounterState(this.counter);
}
// Bloc
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(CounterState(0)) {
on<IncrementEvent>((event, emit) {
emit(CounterState(state.counter + 1));
});
}
}Vorteile:
- ✅ Sehr strukturiert
- ✅ Leicht zu testen
- ✅ Gute Dokumentation
Nachteile:
- ❌ Viel Boilerplate-Code
- ❌ Schwieriger für Anfänger
GetX (Einfach & beliebt)
Wann verwenden?
- ✅ Schnelle Entwicklung
- ✅ Wenig Code
- ✅ Kombiniert Zustandsverwaltung, Routing, Abhängigkeitsinjektion
Einfaches Beispiel:
dart
// Controller
class CounterController extends GetxController {
int counter = 0;
void increment() {
counter++;
update(); // Benachrichtigt UI
}
}
// Verwendung
class CounterPage extends StatelessWidget {
final controller = Get.put(CounterController());
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: <Widget>[
GetBuilder<CounterController>(
builder: (c) => Text('${c.counter}'),
),
ElevatedButton(
onPressed: controller.increment,
child: const Text('Erhöhen'),
),
],
),
),
);
}
}Vorteile:
- ✅ Sehr einfach
- ✅ Wenig Code
- ✅ Routing & Abhängigkeitsinjektion inklusive
Nachteile:
- ❌ Weniger "Flutter-idomatisch"
- ❌ Schwieriger zu debuggen
Riverpod (Moderne Alternative)
Wann verwenden?
- ✅ Moderne Zustandsverwaltung
- ✅ Typsicher
- ✅ Testbar
- ✅ Keine
BuildContext-Abhängigkeit
Einfaches Beispiel:
dart
// Provider
final counterProvider = StateProvider<int>((ref) => 0);
// Verwendung
class CounterPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final counter = ref.watch(counterProvider);
return Scaffold(
body: Center(
child: Column(
children: <Widget>[
Text('$counter'),
ElevatedButton(
onPressed: () {
ref.read(counterProvider.notifier).state++;
},
child: const Text('Erhöhen'),
),
],
),
),
);
}
}Vorteile:
- ✅ Typsicher
- ✅ Testbar
- ✅ Keine
BuildContext-Abhängigkeit
Nachteile:
- ❌ Lernkurve (neue Konzepte)
- ❌ Boilerplate-Code
19.2 Flutter Desktop-Anwendungen (Windows, Mac, Linux)
Vorbereitung
Aktivieren Sie Desktop-Unterstützung:
bash
flutter config --enable-windows-desktop
flutter config --enable-macos-desktop
flutter config --enable-linux-desktopProjekt für Desktop konfigurieren:
bash
# Neues Projekt mit Desktop-Unterstützung erstellen
flutter create --platforms=windows,macos,linux mein_desktop_projekt
# Bestehendes Projekt aktualisieren
flutter create .Windows-App erstellen
windows/runner/main.cpp bearbeiten:
cpp
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <windows.h>
int APIENTRY wWinMain(_In_ int argc,
_In_reads_(argc) wchar_t** argv) {
// Flutter-Projekt initialisieren
flutter::DartProject project(L".");
// Flutter-View-Controller erstellen
flutter::FlutterViewController controller(project);
// Fenster erstellen
// ...
return 0;
}windows/runner/Runner.rc bearbeiten (App-Informationen):
// App-Name
1 VERSIONINFO
FILEVERSION 1,0,0,0
PRODUCTVERSION 1,0,0,0
FILEFLAGSMASK 0x3fL
FILEFLAGS 0x0L
FILEOS 0x40004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040704b0"
BEGIN
VALUE "CompanyName", "Meine Firma"
VALUE "FileDescription", "Meine Desktop-App"
VALUE "FileVersion", "1.0.0.0"
VALUE "InternalName", "meine_app"
VALUE "LegalCopyright", "Copyright (C) 2024"
VALUE "OriginalFilename", "meine_app.exe"
VALUE "ProductName", "Meine App"
VALUE "ProductVersion", "1.0.0.0"
END
END
ENDMac-App erstellen
macos/Runner/Info.plist bearbeiten:
xml
<key>CFBundleName</key>
<string>Meine App</string>
<key>CFBundleDisplayName</key>
<string>Meine App</string>
<key>CFBundleIdentifier</key>
<string>com.beispiel.meineapp</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>macos/Runner.xcworkspace in Xcode öffnen:
- Signierungskonfiguration vornehmen
- App bauen (
Product → Archive) - App veröffentlichen
Linux-App erstellen
linux/main.cc bearbeiten:
cpp
#include <flutter_linux/flutter_linux.h>
int main(int argc, char** argv) {
// Flutter-Projekt initialisieren
g_autoptr<FlDartProject> project = fl_dart_project_new();
// Flutter-View erstellen
FlView* view = fl_view_new(project);
// Fenster erstellen und anzeigen
// ...
return 0;
}App paketieren (Snap, Deb, etc.):
bash
# Snap-Paket erstellen
snapcraft init
snapcraft
# Deb-Paket erstellen
dpkg-deb --build mein_paket19.3 Flutter Mini-Programm-Entwicklung (WeChat Mini-Programm)
flutter_wechat (Plugin)
Installation:
yaml
dependencies:
flutter_wechat: ^0.1.0Verwendung:
dart
import 'package:flutter_wechat/flutter_wechat.dart';
void login() async {
final result = await FlutterWechat.login();
print('Login-Ergebnis: $result');
}Offizielle WeChat Mini-Programm-Dokumentation
Wichtigste Konzepte:
- WXML (ähnlich wie HTML)
- WXSS (ähnlich wie CSS)
- JavaScript (Logik)
- WeChat API (Login, Bezahlung, etc.)
Flutter mit WeChat Mini-Programm kombinieren:
- ✅ Flutter für die App-Entwicklung verwenden
- ✅ WeChat Mini-Programm für leichte Funktionen verwenden
- ✅ API-Kommunikation zwischen App und Mini-Programm
19.4 Flutter native Interaktion (mit Android/iOS nativen Code kommunizieren)
MethodChannel (Kommunikation zwischen Flutter & nativem Code)
Flutter-Seite (Dart-Code):
dart
import 'package:flutter/services.dart';
static const platform = MethodChannel('samples.flutter.dev/battery');
Future<void> getBatteryLevel() async {
try {
final result = await platform.invokeMethod<int>('getBatteryLevel');
print('Batteriestand: $result%');
} on PlatformException catch (e) {
print('Fehler: ${e.message}');
}
}Android-Seite (Java-Code):
java
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "samples.flutter.dev/battery";
@Override
public void configureFlutterEngine(FlutterEngine flutterEngine) {
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setMethodCallHandler(
(call, result) -> {
if (call.method.equals("getBatteryLevel")) {
int batteryLevel = getBatteryLevel();
result.success(batteryLevel);
} else {
result.notImplemented();
}
}
);
}
private int getBatteryLevel() {
// Batteriestand abrufen
return 100; // Beispiel
}
}iOS-Seite (Swift-Code):
swift
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
let batteryChannel = FlutterMethodChannel(name: "samples.flutter.dev/battery",
binaryMessenger: controller.binaryMessenger)
batteryChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
if call.method == "getBatteryLevel" {
self.getBatteryLevel(result: result)
} else {
result(FlutterMethodNotImplemented)
}
})
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func getBatteryLevel(result: FlutterResult) {
// Batteriestand abrufen
result(100) // Beispiel
}
}19.5 Flutter Performance-Optimierung fortgeschritten (Tiefe Optimierung, Ruckeln lösen)
1. const Konstruktoren verwenden
Schlecht:
dart
Text('Hallo') // Jedes Mal neu erstelltGut:
dart
const Text('Hallo') // Einmal erstellt, wiederverwendet2. ListView.builder statt ListView.children verwenden
Schlecht (alle Elemente werden auf einmal erstellt):
dart
ListView(
children: List.generate(1000, (index) => ListTile(title: Text('Element $index'))),
)Gut (nur sichtbare Elemente werden erstellt):
dart
ListView.builder(
itemCount: 1000,
itemBuilder: (context, index) => ListTile(title: Text('Element $index')),
)3. Bilder cachen (cached_network_image)
Installation:
yaml
dependencies:
cached_network_image: ^3.3.0Verwendung:
dart
CachedNetworkImage(
imageUrl: 'https://example.com/bild.jpg',
placeholder: (context, url) => const CircularProgressIndicator(),
errorWidget: (context, url, error) => const Icon(Icons.error),
)4. setState() sparsam verwenden
Schlecht (gesamte Seite wird neu gebaut):
dart
void _erhoehen() {
setState(() {
_zaehler++;
_aktualisiereText(); // Unnötig, da es nicht die UI beeinflusst
});
}Gut (nur notwendige Teile aktualisieren):
dart
void _erhoehen() {
setState(() {
_zaehler++;
});
}
void _aktualisiereText() {
// Logik, die kein setState() benötigt
}5. AnimatedBuilder oder ValueListenableBuilder verwenden
Für komplexe Animationen/Zustandsänderungen:
dart
ValueListenableBuilder<int>(
valueListenable: _zaehler,
builder: (context, wert, child) {
return Text('$wert');
},
)6. RepaintBoundary verwenden
Verwendung:
dart
RepaintBoundary(
child: Container(
width: 100,
height: 100,
color: Colors.red,
),
)Wann verwenden?
- ✅ Komplexe Widgets, die sich oft ändern
- ✅ Animationen
- ✅ Bilder
Zusammenfassung
In diesem Kapitel haben Sie:
- ✅ Erweiterte Zustandsverwaltung (Bloc, GetX, Riverpod) kennengelernt
- ✅ Flutter Desktop-Anwendungen (Windows, Mac, Linux) erstellt
- ✅ Flutter Mini-Programm-Entwicklung (WeChat Mini-Programm) kennengelernt
- ✅ Flutter native Interaktion (mit Android/iOS nativen Code kommunizieren) gelernt
- ✅ Flutter Performance-Optimierung fortgeschritten (Tiefe Optimierung, Ruckeln lösen) gelernt
Nächstes Kapitel: Wir werden Lernressourcen lernen (offizielle Dokumenten, Online-Übungen, Videos).
Übungsaufgaben:
- Implementieren Sie Zustandsverwaltung mit Bloc für ein komplexes Projekt
- Erstellen Sie eine Desktop-Anwendung (Windows, Mac oder Linux)
- Implementieren Sie native Interaktion mit MethodChannel (Batteriestand abrufen)
- Optimieren Sie die Performance einer App mit komplexen Animationen
Häufige Fehler:
- ❌ Zustandsverwaltung zu komplex machen für einfache Apps → Bei einfachen Zuständen
setState()verwenden! - ❌ Desktop-Anwendung ohne Konfiguration bauen → Desktop-Unterstützung aktivieren!
- ❌
constKonstruktoren vergessen → Performance-Probleme! - ❌
ListViewohnebuilderfür große Datenmengen verwenden → Performance-Probleme!
