OnSharedPreferenceChangeListener for Flutter - flutter

In Android, you can do the following to listen to shared preference change
SharedPreferences.OnSharedPreferenceChangeListener spChanged = new
SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
// your stuff here
}
};
Is it possible to do this using flutter? I have read through the official flutter shared_preference and this features seems not yet implemented.
Is there any other library or ways to achieve the above without diving into native code. Thanks.

You can easily "listen" to SharedPreferences using a package like flutter_riverpod.
Initialize sharedPreferences
SharedPreferences? sharedPreferences;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
sharedPreferences = await SharedPreferences.getInstance();
runApp(const ProviderScope(child: MyApp()));
}
Create the stateProvider
import 'package:hooks_riverpod/hooks_riverpod.dart';
final keepOnTopProvider = StateProvider<bool>((ref) {
return sharedPreferences?.getBool('on_top') ?? true;
});
Update your UI when something changes
class SettingsView extends ConsumerWidget {
const SettingsView({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
bool onTop = ref.watch(keepOnTopProvider);
return Scaffold(
appBar: AppBar(title: const Text('Settings'), centerTitle: false),
body: ListView(
padding: const EdgeInsets.symmetric(horizontal: 12),
children: [
SwitchListTile(
title: const Text('Keep on top'),
value: onTop,
onChanged: (value) async {
sharedPreferences?.setBool('on_top', value);
ref.read(keepOnTopProvider.notifier).state = value;
await windowManager.setAlwaysOnTop(value);
},
),
],
),
);
}
}

As a work around, add the following codes to your main():
void funTimerMain() async {
// here check any changes to SharedPreferences, sqflite, Global Variables etc...
if (bolAnythingChanged) {
// do something
// 'refresh' any page you want (below line using Redux as example)
GlobalVariables.storeHome.dispatch(Actions.Increment);
}
// recall this timer every x milliseconds
new Future.delayed(new Duration(milliseconds: 1000), () async {
funTimerMain();
});
}
// call the timer for the first time
funTimerMain();

Related

How to use a variable within a method elsewhere in Flutter?

I know this is a stupid question, but I'm asking as a newbie to flutter.
I created a getData() method to call Firebase's User data and display it on the app. And to call it, result.data() is saved as a variable name of resultData.
But as you know I can't use Text('user name: $resultData'). How do I solve this? It's a difficult problem for me, since I don't have any programming basics. thank you.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:shipda/screens/login/login_screen.dart';
import 'package:get/get.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final _authentication = FirebaseAuth.instance;
User? loggedUser;
final firestore = FirebaseFirestore.instance;
void getData() async {
var result = await firestore.collection('user').doc('vUj4U27JoAU6zgFDk6sSZiwadQ13').get();
final resultData = result.data();
}
#override
void initState() {
super.initState();
getCurrentUser();
getData();
}
void getCurrentUser(){
try {
final user = _authentication.currentUser;
if (user != null) {
loggedUser = user;
print(loggedUser!.email);
}
} catch (e){
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
children: [
Text('Home Screen'),
IconButton(
onPressed: () {
FirebaseAuth.instance.signOut();
Get.to(()=>LoginScreen());
},
icon: Icon(Icons.exit_to_app),
),
IconButton(
onPressed: () {
Get.to(() => LoginScreen());
},
icon: Icon(Icons.login),
),
Text('UserInfo'),
Text('user name: ')
],
),
),
);
}
}
What you are referring to is called state.
It is a complex topic and you will have to start studying it to correctly develop any web based app.
Anyway, as for your situation, you should have resultData be one of the attributes of the _HomeScreenState class.
Then change resultData in a setState method, like this:
setState(() {
resultData = result.data();
});
Then, in the Text widget, you can actually do something like:
Text("My data: " + resultData.ToString())
Instead of ToString of course, use anything you need to actually access the data.
By writing
void getData() async {
var result = await firestore.collection('user').doc('vUj4U27JoAU6zgFDk6sSZiwadQ13').get();
final resultData = result.data();
}
you make resultData only local to the function getData(). You should declare it outside. Also you need to put it in a setState to make it rebuild the screen after loading. I don't know what type it is, but if it's a String for example you could write
String? resultData;
void getData() async {
var result = await firestore.collection('user').doc('vUj4U27JoAU6zgFDk6sSZiwadQ13').get();
setState(() {
resultData = result.data();
});
}
Then you can use Text('user name: $resultData') for example

Flutter - Load variables with SharedPreferences

I am learning how to use the SharedPreferences library in Flutter.
I created this code and I would like the counter and counter2 variables once I close and reopen the app to remain as the last save.
However, when I reopen the app the counter and counter2 values return to 0.
Can anyone explain to me where I am going wrong?
Thank you.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'data.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int counter = 0;
int counter2 = 0;
increment() {
setState(() {
counter += 1;
counter2 += 2;
});
}
loadData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
String? json = prefs.getString('UserData');
print('loaded json: $json');
if (json == null) {
print('NO DATA (null)');
} else {
Map<String, dynamic> map = jsonDecode(json);
print('map $map');
final data = Data.fromJson(map);
print('Data ${data.counter}, ${data.counter2}');
}
});
}
saveData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final _data = Data(counter: counter, counter2: counter2);
String json = jsonEncode(_data);
print('saved json: $json');
prefs.setString('UserData', json);
}
clearData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.clear();
print('data cleared');
}
/// dichiarare l' initState()
#override
void initState() {
super.initState();
loadData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'You have pushed the button this many times:',
),
Text(
'c: $counter, c2: $counter2',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
increment();
saveData();
},
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class Data {
int counter = 0;
int counter2 = 0;
Data({required this.counter, required this.counter2});
Map<String, dynamic> toJson() {
return {
'counter': counter,
'counter2': counter2,
};
}
Data.fromJson(Map<String, dynamic> json) {
counter = json['counter'];
counter2 = json['counter2'];
}
}
I agree with the other answer, the best is to use a FutureBuilder. But you can make your current code work with simply adding two lines at the end of loadData:
loadData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
String? json = prefs.getString('UserData');
print('loaded json: $json');
if (json == null) {
print('NO DATA (null)');
} else {
Map<String, dynamic> map = jsonDecode(json);
print('map $map');
final data = Data.fromJson(map);
print('Data ${data.counter}, ${data.counter2}');
// add these lines
counter = data.counter;
counter2 = data.counter2;
}
});
}
What happens (as the other answer says) is that your widget is first built without knowing the values from SharedPreferences. After a little time this first build is done, the loadData future completes, and with setState the widget is rebuilt.
In a real application you'd like to avoid unnecessary builds, so you'd rather display a progress indicator while async data is being loaded, check FutureBuilder.
A short answer is that when you call loadData(); inside initState the function is performed asynchronously relative to the rest of the widget, so your Scaffold is built before the data is available. This is why you are seeing the data in from your print but not in the app.
One way to address it is to us a https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html

SharedPreference does not read local data in Flutter

In a Flutter project, on clicking the "SignUp" button, I need 2 things,
Saving data to cloud
Saving data to local storage.
I user SharedPreferece to save the data and to retrieve it. The problem is, the data I saved into local storage is available immediately after I Sign Up, but if I hot reload the emulator, the data shows null!
The function by which I saved the data to both cloud and local storage:
Future <void> _saveDataToFirestore(User? currentUser) async{
await FirebaseFirestore.instance.collection("sellers").doc(currentUser!.uid).set({
"sellerUID": currentUser.uid,
"sellerName": _fullNameController.text.trim(),
"sellerAvatarUrl": sellerImageUrl,
"sellerEmail": currentUser.email,
"phone": _phoneNumberController.text.trim(),
"address": completeAddress,
"status": "approved",
"earnings": 0.0,
"lat": position!.latitude,
"lng": position!.longitude
});
// save 3 data locally
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
await sharedPreferences.setString("uid", currentUser.uid);
await sharedPreferences.setString("email", currentUser.email.toString()); // do not take from controllers, because it will not be null if sign up fail
await sharedPreferences.setString("name", _fullNameController.text);
await sharedPreferences.setString("image", sellerImageUrl);
print("${currentUser.uid},${currentUser.email.toString()}, ${_fullNameController.text}, ${sellerImageUrl} ");}
I initialized SharedPreference in main.dart
Future <void> main() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences.setMockInitialValues({});
await SharedPreferences.getInstance();
await Firebase.initializeApp();
runApp(const MyApp());
}
The home_screen where I needed the local storage data
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:food_fancy_chef/authentication/auth_screen.dart';
import 'package:food_fancy_chef/authentication/login.dart';
import 'package:food_fancy_chef/authentication/register.dart';
import 'package:food_fancy_chef/global/global.dart';
import 'package:shared_preferences/shared_preferences.dart';
class HomeScreen extends StatefulWidget {
static const routeName = "home_screen";
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
String sharedName = "Null value";
Future<void> _getIntFromSharedPref()async{
final pref = await SharedPreferences.getInstance();
final startupName = pref.getString("name");
if(startupName == null){
sharedName = "no name";
} else{
setState(() {
sharedName = startupName;
});
}
}
#override
void initState() {
_getIntFromSharedPref();
super.initState();
}
#override
void didChangeDependencies() {
_getIntFromSharedPref();
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
flexibleSpace: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.cyan,
Colors.amber
],
begin: FractionalOffset(0.0, 0.0),
end: FractionalOffset(1.0, 1.6),
stops: [0.0,1.0],
tileMode: TileMode.mirror
)
),
),
title: Text(
sharedName // I need "name" from local storage
// sharedPreferences!.getString("name")! == null? "null value":sharedPreferences!.getString("name")!
)
),
body: Column(
children: [
Center(
child: ElevatedButton(
onPressed: (){
setState(() {
firebaseAuth.signOut();
Navigator.pushReplacement(context, MaterialPageRoute(builder: (c)=> LoginScreen()));
});
},
child: Text("Log Out"),
),
),
Center(
child: ElevatedButton(
onPressed: (){
setState(() {
firebaseAuth.signOut();
Navigator.pushReplacement(context, MaterialPageRoute(builder: (c)=> AuthScreen()));
});
},
child: Text("signout"),
),
),
],
),
);
}
}
What I have tried:
I have tried to get the data directly without any init() method, it returns null:
sharedPreferences!.getString("name")! == null? "null value":sharedPreferences!.getString("name")!
I have declared the variable first and assigned the value via a function run at init(), code is below.
I tried the same process above but with didChangeDependencies() method.
I used both init() and didChangeDependencies()
I deleted the emulator and reinstalled it.
I also saved SharePreferences() in a global.dart file, so that, I can access them anywhere in the project.
Try this
Future<void> _getIntFromSharedPref() async{
SharedPreferences? pref = await SharedPreferences.getInstance();
String startupName = pref!.getString("name") ?? 'no name';
setState(() {});
}
Try removing SharedPreferences.setMockInitialValues({}); from main.dart.
When hot restart the widget tree re built it means only this
Widget build(BuildContext context) method triggered. initState is called only once for a widget and didChangeDependencies may be called multiple times per widget lifecycle. So initializing some thing on didChangeDependencies you have to be careful. Calling like this will resolve your issue whille hot restart
#override
Widget build(BuildContext context) {
_getIntFromSharedPref();
return Scaffold(
);
}

Keep VideoPlayerController playing audio when closing the app

I am trying to develop an app that presents videos to the user. I am using VideoPlayerController for loading the videos, and ChewieController for the UI.
It works great, but when the user closes the app, the video stops. I would like the video to keep playing its audio even when closing the app/locking the device.
I couldn't find anything about it on the VideoPlayerController and in the ChewieController documentations.
Is this functionality possible in Flutter and Dart?
Thank you!
Unfortunately Flutter's video_player package doesn't support background video or audio playing. But you can use flutter_playout which wraps ExoPlayer on Android and AVPlayer framework on iOS with the ability to playback video in background or even lock screen. You can find out more about it here. Below is an example code provided by library's GitHub repo which plays a video and it keeps playing in background
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_playout/multiaudio/HLSManifestLanguage.dart';
import 'package:flutter_playout/multiaudio/MultiAudioSupport.dart';
import 'package:flutter_playout/player_observer.dart';
import 'package:flutter_playout/player_state.dart';
import 'package:flutter_playout/video.dart';
import 'package:flutter_playout_example/hls/getManifestLanguages.dart';
class VideoPlayout extends StatefulWidget {
final PlayerState desiredState;
final bool showPlayerControls;
const VideoPlayout({Key key, this.desiredState, this.showPlayerControls})
: super(key: key);
#override
_VideoPlayoutState createState() => _VideoPlayoutState();
}
class _VideoPlayoutState extends State<VideoPlayout>
with PlayerObserver, MultiAudioSupport {
final String _url = null;
List<HLSManifestLanguage> _hlsLanguages = List<HLSManifestLanguage>();
#override
void initState() {
super.initState();
Future.delayed(Duration.zero, _getHLSManifestLanguages);
}
Future<void> _getHLSManifestLanguages() async {
if (!Platform.isIOS && _url != null && _url.isNotEmpty) {
_hlsLanguages = await getManifestLanguages(_url);
setState(() {});
}
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
/* player */
AspectRatio(
aspectRatio: 16 / 9,
child: Video(
autoPlay: true,
showControls: widget.showPlayerControls,
title: "MTA International",
subtitle: "Reaching The Corners Of The Earth",
preferredAudioLanguage: "eng",
isLiveStream: true,
position: 0,
url: _url,
onViewCreated: _onViewCreated,
desiredState: widget.desiredState,
),
),
/* multi language menu */
_hlsLanguages.length < 2 && !Platform.isIOS
? Container()
: Container(
child: Row(
children: _hlsLanguages
.map((e) => MaterialButton(
child: Text(
e.name,
style: Theme.of(context)
.textTheme
.button
.copyWith(color: Colors.white),
),
onPressed: () {
setPreferredAudioLanguage(e.code);
},
))
.toList(),
),
),
],
),
);
}
void _onViewCreated(int viewId) {
listenForVideoPlayerEvents(viewId);
enableMultiAudioSupport(viewId);
}
#override
void onPlay() {
// TODO: implement onPlay
super.onPlay();
}
#override
void onPause() {
// TODO: implement onPause
super.onPause();
}
#override
void onComplete() {
// TODO: implement onComplete
super.onComplete();
}
#override
void onTime(int position) {
// TODO: implement onTime
super.onTime(position);
}
#override
void onSeek(int position, double offset) {
// TODO: implement onSeek
super.onSeek(position, offset);
}
#override
void onDuration(int duration) {
// TODO: implement onDuration
super.onDuration(duration);
}
#override
void onError(String error) {
// TODO: implement onError
super.onError(error);
}
}
As the video_player package now has the allowBackgroundPlayback option, I created this simple example showing how to integrate video_player and audio service.
example_video_player.dart
// This example demonstrates a simple video_player integration.
import 'dart:async';
import 'package:audio_service/audio_service.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
// You might want to provide this using dependency injection rather than a
// global variable.
late AudioPlayerHandler _audioHandler;
Future<void> main() async {
_audioHandler = await AudioService.init(
builder: () => AudioPlayerHandler(),
config: const AudioServiceConfig(
androidNotificationChannelId: 'com.ryanheise.myapp.channel.audio',
androidNotificationChannelName: 'Audio playback',
androidNotificationOngoing: true,
),
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Audio Service Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const MainScreen(),
);
}
}
class MainScreen extends StatefulWidget {
const MainScreen({Key? key}) : super(key: key);
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
late VideoPlayerController _controller;
#override
void initState() {
super.initState();
_controller = VideoPlayerController.network('https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
videoPlayerOptions: VideoPlayerOptions(allowBackgroundPlayback: true))
..initialize().then((_) {
_audioHandler.setVideoFunctions(_controller.play, _controller.pause, _controller.seekTo, () {
_controller.seekTo(Duration.zero);
_controller.pause();
});
// So that our clients (the Flutter UI and the system notification) know
// what state to display, here we set up our audio handler to broadcast all
// playback state changes as they happen via playbackState...
_audioHandler.initializeStreamController(_controller);
_audioHandler.playbackState.addStream(_audioHandler.streamController.stream);
// Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
setState(() {});
});
}
#override
void dispose() {
// Close the stream
_audioHandler.streamController.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Audio Service Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: _controller.value.isInitialized
? AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: VideoPlayer(_controller),
)
: Container(),
),
// Play/pause/stop buttons.
StreamBuilder<bool>(
stream: _audioHandler.playbackState.map((state) => state.playing).distinct(),
builder: (context, snapshot) {
final playing = snapshot.data ?? false;
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_button(Icons.fast_rewind, _audioHandler.rewind),
if (playing) _button(Icons.pause, _audioHandler.pause) else _button(Icons.play_arrow, _audioHandler.play),
_button(Icons.stop, _audioHandler.stop),
_button(Icons.fast_forward, _audioHandler.fastForward),
],
);
},
),
// Display the processing state.
StreamBuilder<AudioProcessingState>(
stream: _audioHandler.playbackState.map((state) => state.processingState).distinct(),
builder: (context, snapshot) {
final processingState = snapshot.data ?? AudioProcessingState.idle;
return Text("Processing state: ${(processingState)}");
},
),
],
),
),
);
}
IconButton _button(IconData iconData, VoidCallback onPressed) => IconButton(
icon: Icon(iconData),
iconSize: 64.0,
onPressed: onPressed,
);
}
class MediaState {
final MediaItem? mediaItem;
final Duration position;
MediaState(this.mediaItem, this.position);
}
/// An [AudioHandler] for playing a single item.
class AudioPlayerHandler extends BaseAudioHandler with SeekHandler {
late StreamController<PlaybackState> streamController;
static final _item = MediaItem(
id: 'https://s3.amazonaws.com/scifri-episodes/scifri20181123-episode.mp3',
album: "Science Friday",
title: "A Salute To Head-Scratching Science",
artist: "Science Friday and WNYC Studios",
duration: const Duration(milliseconds: 5739820),
artUri: Uri.parse('https://media.wnyc.org/i/1400/1400/l/80/1/ScienceFriday_WNYCStudios_1400.jpg'),
);
Function? _videoPlay;
Function? _videoPause;
Function? _videoSeek;
Function? _videoStop;
void setVideoFunctions(Function play, Function pause, Function seek, Function stop) {
_videoPlay = play;
_videoPause = pause;
_videoSeek = seek;
_videoStop = stop;
mediaItem.add(_item);
}
/// Initialise our audio handler.
AudioPlayerHandler();
// In this simple example, we handle only 4 actions: play, pause, seek and
// stop. Any button press from the Flutter UI, notification, lock screen or
// headset will be routed through to these 4 methods so that you can handle
// your audio playback logic in one place.
#override
Future<void> play() async => _videoPlay!();
#override
Future<void> pause() async => _videoPause!();
#override
Future<void> seek(Duration position) async => _videoSeek!(position);
#override
Future<void> stop() async => _videoStop!();
void initializeStreamController(VideoPlayerController? videoPlayerController) {
bool _isPlaying() => videoPlayerController?.value.isPlaying ?? false;
AudioProcessingState _processingState() {
if (videoPlayerController == null) return AudioProcessingState.idle;
if (videoPlayerController.value.isInitialized) return AudioProcessingState.ready;
return AudioProcessingState.idle;
}
Duration _bufferedPosition() {
DurationRange? currentBufferedRange = videoPlayerController?.value.buffered.firstWhere((durationRange) {
Duration position = videoPlayerController.value.position;
bool isCurrentBufferedRange = durationRange.start < position && durationRange.end > position;
return isCurrentBufferedRange;
});
if (currentBufferedRange == null) return Duration.zero;
return currentBufferedRange.end;
}
void _addVideoEvent() {
streamController.add(PlaybackState(
controls: [
MediaControl.rewind,
if (_isPlaying()) MediaControl.pause else MediaControl.play,
MediaControl.stop,
MediaControl.fastForward,
],
systemActions: const {
MediaAction.seek,
MediaAction.seekForward,
MediaAction.seekBackward,
},
androidCompactActionIndices: const [0, 1, 3],
processingState: _processingState(),
playing: _isPlaying(),
updatePosition: videoPlayerController?.value.position ?? Duration.zero,
bufferedPosition: _bufferedPosition(),
speed: videoPlayerController?.value.playbackSpeed ?? 1.0,
));
}
void startStream() {
videoPlayerController?.addListener(_addVideoEvent);
}
void stopStream() {
videoPlayerController?.removeListener(_addVideoEvent);
streamController.close();
}
streamController = StreamController<PlaybackState>(onListen: startStream, onPause: stopStream, onResume: startStream, onCancel: stopStream);
}
}
I've been using the better_player package. It's quite good uses video_player and chewie and also has support for player notification and PiP.
And don't forget to enable the background audio capability on your xcode.
xcode-audio-capability

Flutter - Drawer as sub-class not updating

I'm a fairly inexperienced coder.
I have a Drawer which I have created as a separate class. The issue I'm having is the dynamic data for the Drawer is not populating.
I am expecting the data being retrieved from Shared Preferences should populate the third line of my view with the value of currUserL.
It's being evaluated correctly, and returns the value of currUserL to the console, but is not updated in the Drawer.
I've loaded up a about button (triggering the update method) that works when pressed manually, but data persists only while the drawer remains open. It reverts when the drawer is closed.
drawerPatient.dart
class DrawerPatient extends StatefulWidget {
DrawerPatient({Key key}) : super(key: key);
#override
_DrawerPatientState createState() => new _DrawerPatientState();
}
class _DrawerPatientState extends State<DrawerPatient> {
String currUserL = "nv3";
Future getPref() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
currUserL = prefs.getString('currUserLast');
debugPrint('user: $currUserL');
}
#override
void initState() {
getPref();
}
void update() {
setState(() {
getPref();
});
}
#override
Widget build(BuildContext context) {
return Drawer(
child: new ListView(
children: <Widget>[
new DrawerHeader(
child: new Text('Patient Management'),
),
new ListTile(
title: new Text('search'),
onTap: () {},
),
new ListTile(
title: new Text(currUserL),
onTap: () {},
),
new Divider(),
new ListTile(
title: new Text('About'),
onTap: update,
),
],
));
}
}
userList.dart
class UserList extends StatefulWidget {
UserList({Key key, this.title}) : super(key: key);
final String title;
final String titleHead = "User List";
#override
_UserListState createState() => new _UserListState();
}
class _UserListState extends State<UserList> {
: sortStr}, headers: {"Accept": "application/json"});
setState(() {
data = json.decode(response.body);
});
}
#override
void initState() {
this.makeRequest();
// DrawerPatient().createState().update();
}
void _refresh() {
setState(() {});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Patient List"),
drawer: new DrawerPatient(key: new UniqueKey()),
...
Drawer when opened
Drawer after clicking about (update)
So I found the answer, thanks to #Dinesh for pointing me in the right direction.
The answer was to put the setState as a dependency on the async get prefs.
Future getPref() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
currUserI = prefs.getString('currUserId');
currUserF = prefs.getString('currUserFirst');
currUserL = prefs.getString('currUserLast');
debugPrint('user: $currUserL');
});
}
Can you try this,
Future getCurrentUser() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString('currUserLast');
}
void update() {
val tempName = getCurrentUser();
setState(() {
currUserL = tempName;
});
}
Reason: Basically wait for the async method before calling setState