Dane Mackier
Dane MackierFlutter Consultant @ MDK Studio

Jak zbudować menadżera motywów we Flutterze

Sprawdź, jak krok po kroku stworzyć menadżera motywów we Flutterze dzięki ThemeManager.
4.07.20193 min
Jak zbudować menadżera motywów we Flutterze

W tym poradniku zbudujemy prostego menadżer motywów, by móc zrobić coś takiego:

Przyjrzymy się jak zmienić kolor paska stanu, jak również całego motywu za pomocą flutter_statusbarcolor i provider. Zacznijmy od zainstalowania pakietów.

flutter_statusbarcolor: any
provider: ^3.0.0


Następnie możemy stworzyć HomeView, który wyświetli nasze zmiany w motywach.

class HomeView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).backgroundColor,
floatingActionButton: FloatingActionButton(
onPressed: () {

},
));
}
}

Menedżer Motywów

Zalecam, aby odpowiedzialności obiektów były jasno zdefiniowane i aby Twój kod to odzwierciedlał. W naszym przypadku odpowiedzialność za zmianę motywów będzie spoczywać na ThemeManager. UI będzie po prostu używało motywu i ustawiało go bez wiedzy o zmianach jakie w nim zaszły. Nasz menedżer motywów będzie miał predefiniowaną listę availableThemes, jak również kontroler, w którym będziemy wyświetlać nowy motyw gdy zostanie ustawiony. Wyeksponujemy również strumień kontrolera poprzez właściwość publiczną.

class ThemeManager {
StreamController<ThemeData> _themeController = StreamController<ThemeData>();

List<ThemeData> _availableThemes = [
ThemeData(backgroundColor: Colors.red, accentColor: Colors.blue),
ThemeData(backgroundColor: Colors.green, accentColor: Colors.yellow),
ThemeData(backgroundColor: Colors.purple, accentColor: Colors.pink),
ThemeData(backgroundColor: Colors.blue, accentColor: Colors.red),
];

Stream<ThemeData> get theme => _themeController.stream;
}

Aktualizacja koloru paska stanu (StatusBar)

Zmiana motywu będzie obejmowała aktualizację koloru paska stanu, więc zaimplementujemy to jako oddzielną funkcję, którą wywołamy podczas zmiany naszego motywu.

Future _updateStatusBarColor(ThemeData themeToApply) async {
// Set status bar color
await FlutterStatusbarcolor.setStatusBarColor(themeToApply.accentColor);
// Check the constrast between the colors and set the status bar icons colors to white or dark

if (useWhiteForeground(themeToApply.accentColor)) {
FlutterStatusbarcolor.setStatusBarWhiteForeground(true);
} else {
FlutterStatusbarcolor.setStatusBarWhiteForeground(false);
}
}


Tutaj ustawiamy StatusBarColor na accentColor motywu. Dodatkowo upewniamy się, że ikony paska stanu są nadal widoczne, więc sprawdzamy kontrast za pomocą useWhiteForeground i ustawiamy ikony na biało lub czarno.

Aktualizacja motywu

Aby zmienić motyw, będziemy po prostu śledzić indeks currentTheme i będziemy inkrementować go w funkcji. Następnie użyjemy nowego indeksu, aby uzyskać motyw i dodać go do kontrolera.

int _currentTheme = 0;

...

Future changeTheme() async {
_currentTheme++;
if (_currentTheme >= _availableThemes.length) {
_currentTheme = 0;
}

// Get the theme to apply
var themeToApply = _availableThemes[_currentTheme];

// Update status bar color
await _updateStatusBarColor(themeToApply);
// Broadcast new theme
_themeController.add(themeToApply);
}

Dostarczenie motywu

Aby przenieść motyw do aplikacji i sprawić, że będzie automatycznie aktualizowany, skorzystamy z Providera. Zaczniemy od opakowania naszego MaterialApp w MultiProvider i dostarczenia ThemeManager jako providera, a następnie Theme jako StreamProvider.

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
Provider.value(value: ThemeManager()),
StreamProvider<ThemeData>(
builder: (context) =>
Provider.of<ThemeManager>(context, listen: false).theme)
],
child: MaterialApp(
title: 'Theme Manager Demo',
home: HomeView(),
));
}
}


Tutaj rejestrujemy ThemeManager jako Provider. Następnie wysyłamy żądanie do providera o ThemeManagera w builderze StreamProvider i zwracamy strumień ThemeData. Mówimy również StreamProvider, aby nie nasłuchiwał aktualizacji z ThemeManagera. Jeśli używasz get_it do wstrzykiwania zależności, to będzie to wyglądać tak.

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider<ThemeData>(builder: (context) => locator<ThemeManager>().theme)
],
child: MaterialApp(
title: 'Theme Manager Demo',
home: HomeView(),
));
}
}


Następnie musimy się upewnić, że gdy ThemeData się zmieni, zaktualizujemy nasz MaterialApp o nowy motyw. W tym celu opakujemy nasz MaterialApp w Konsumenta typu ThemeData i przekażemy motyw do naszej właściwości związanej z motywem w naszej aplikacji.

Widget build(BuildContext context) {
return MultiProvider(
...
child: Consumer<ThemeData>(
builder: (context, theme, child) => MaterialApp(
title: 'Theme Manager Demo',
theme: theme,
home: HomeView(),
)),
);
}


I ostatnią rzeczą, jaką musimy zrobić, jest wywołanie funkcji changeTheme w naszym menedżerze, gdy w HomeView zostanie naciśnięty przycisk FloatingActionButton.

class HomeView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).backgroundColor,
floatingActionButton: FloatingActionButton(
onPressed: () {
Provider.of<ThemeManager>(context).changeTheme();
},
),
);
}
}

Podsumowanie

I to powinno wystarczyć. Należy dołożyć wszelkich starań, aby odpowiedzialności były rozdzielone i jasno zdefiniowane. W przypadku UI pokazywany jest tylko kolor zapewniony przez motyw. ThemeManager jest odpowiedzialny za aktualizację do nowego motywu, wykonywanie wszystkich obliczeń (w tym przykładzie jest ich niewiele), a następnie wyświetlanie nowego motywu. Reszta powinna być obsługiwana przez architekturę, w tym przypadku provider uaktualni motyw, który wyśle żądania do wszystkich konsumentów, zależnych od ThemeData, co doprowadzi do ich przebudowania.


Oryginał tekstu w języku angielskim przeczytasz tutaj.

<p>Loading...</p>