I have a Switch on a screen and I need it to use the value that is in a Provider. I've tried to infer this value using the provider's value, but the Switch is immobile, it doesn't change visually(but the value is changed in the DB), it only works as it should when I remove the provider's inferences.
My Provider: (It is being called when I start the application)
class DailyDatabase with ChangeNotifier {
bool notificationActive = false;
void loadDailyData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
notificationActive = prefs.getBool('notificationActive') ?? false;}
Variable:
@override
Widget build(BuildContext context) {
final provider = Provider.of<DailyDatabase>(context);
_notificationActive = provider.notificationActive;
Switch:
Switch(
value: _notificationActive,
onChanged: (value) {
_notificationActive = value;
provider.setNotification(value);
},
),
Here's a very basic example of Provider
with a Switch
and using StatefulWidget
and its setState
to refresh the widget (instead of using ChangeNotifierProvider
and Consumer
to "listen" and "localize" the widget rebuild to just the Switch
and the Text
label, which is perhaps a more typical use):
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class SwitchProviderPage extends StatefulWidget {
@override
_SwitchProviderPageState createState() => _SwitchProviderPageState();
}
class Database {
bool active = false;
void setActive(bool value) {
active = value;
}
}
class _SwitchProviderPageState extends State<SwitchProviderPage> {
@override
Widget build(BuildContext context) {
return Provider(
create: (context) => Database(),
child: Builder(
builder: (context) {
Database db = Provider.of<Database>(context, listen: false);
return Scaffold(
appBar: AppBar(
title: Text('Switch Field'),
),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Active? ${db.active}'),
Switch(
onChanged: (val) { // ← remember to use val (bool)
print('Switch value: $val');
setState(() {
db.setActive(val);
// this sets the Switch setting on/off
});
},
value: db.active,
)
],
),
),
),
);
},
),
);
}
}
Note:
The use of Builder
in above is only to make Scaffold
be a child of Provider
.
Otherwise, Scaffold
would be a sibling, not a child, and Provider
will not work. Since you wrap your entire app in your ChangeNotifierProvider
, you don't need to do this. I needed to do this to get a self-contained example.
Here's a complete app example (copy paste into main.dart, replacing everything on page) using a StatelessWidget
and the typical/common ChangeNotifierProvider
& Consumer
.
This version uses a mocked long duration async call when flipping Switch
.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(ChangeNotifierProvider<DatabaseListenable>(
create: (context) => DatabaseListenable(),
child: MyApp())
);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Provider Demo App',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: StatelessSwitchProviderPage(),
);
}
}
class DatabaseListenable with ChangeNotifier {
bool active = false;
Future<void> setActive(bool value) async {
// Mock slow database call ↓
await Future.delayed(Duration(milliseconds: 500), () {
active = value;
print('Async DB call DONE.');
});
notifyListeners(); // ← causes Consumer to rebuild
}
}
class StatelessSwitchProviderPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Switch Provider Stateless'),
),
body: SafeArea(
child: Center(
child: Consumer<DatabaseListenable>(
builder: (context, db, child) => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Active? ${db.active}'),
Switch(
onChanged: (val) {
print('Switch value: $val');
db.setActive(val);
},
value: db.active,
)
],
),
),
),
),
);
}
}