I need to get one stored value from shared preferences and put it into text widget. How can I do this without a future builder?
_currPage() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
int page = prefs.getInt('currPage') ?? 0;
return page;
}
class _AllTasksPageState extends State<AllTasksPage> {
#override
Widget build(BuildContext context) {
...
Text(_currPage()); //not working
...
}
}
int page = 0;
#override
void initState() {
super.initState();
readData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('$page'),
),
);
}
void readData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
if (prefs.getInt('currPage') == null)
setState(() => page = 0);
else
setState(() => page = prefs.getInt('currPage')!);
}
create a helper class just for shared preferences
import 'package:shared_preferences/shared_preferences.dart';
class SPHelper {
SPHelper._();
static SPHelper sp = SPHelper._();
SharedPreferences? prefs;
Future<void> initSharedPreferences() async {
prefs = await SharedPreferences.getInstance();
}
Future<void> save(String name, String value) async {
await prefs!.setString(name, value);
}
String? get(String key) {
return prefs!.getString(key);
}
Future<bool> delete(String key) async {
return await prefs!.remove(key);
}
}
in your main function add
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await SPHelper.sp.initSharedPreferences();
...
runApp(MyApp());
...
}
then to get your data just write
SPHelper.sp.get("YOUR_KEY")
and to store your data just write
SPHelper.sp.save("YOUR_KEY","YOUR_VALUE")
This is the best way to use shared preference.
I hope that's will help you in your problem.
The simplest method is using a SharedPreferences provider:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(
MultiProvider(
providers: [
Provider.value(value: await SharedPreferences.getInstance()),
],
child: MaterialApp(
home: AllTasksPage(),
),
),
);
}
class AllTasksPage extends StatelessWidget {
const AllTasksPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final page = context.read<SharedPreferences>().getInt('currPage') ?? 0;
return Scaffold(body: Text('$page'));
}
}
If you don't want to use a future builder, the other solution is if you have a variable that tells you that are you still waiting/loading data and if yes, show a waiting screen:
class _AllTasksPageState extends State<AllTasksPage> {
bool _loading = true;
String? textValue; // String textValue = "";
#override
initState() {
super.initState();
setTextValue();
}
setTextValue() {
SharedPreferences prefs = await SharedPreferences.getInstance();
int page = prefs.getInt('currPage') ?? 0;
setState(() {
textValue = "$page";
_loading = false;
});
}
// then in the build method
#override
Widget build(BuildContext context) {
return _loading ? CircularProgressIndicator() : actualScreen();
}
}
Related
I'm try to learn how to get token. What is wrong with the code I wrote?
I'm try to learn how to get token.
import 'package:shared_preferences/shared_preferences.dart';
import '../common/constant.dart';
Use this sample:
class UtilSharedPreferences {
static Future<String> getToken() async {
var prefs = await SharedPreferences.getInstance();
return prefs.getString('Token') ?? '';
}
static Future<bool> setToken(String value) async {
var prefs = await SharedPreferences.getInstance();
return prefs.setString('Token', value);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({
Key? key,
}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String str = '';
gettoken() async {
str = await UtilSharedPreferences.getToken();
setState(() {});
}
#override
void initState() {
// TODO: implement initState
super.initState();
gettoken();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('Token :$str'),
Center(
child: InkWell(
onTap: () {
var result = UtilSharedPreferences.setToken('hi');
if(result){
print('success');
}
},
child: Text('save token'),
),
),
],
),
);
}
}
Future is asynchronous and you must use await to get the desired value.
user = await UtilSharedPreferencs();
The async keyword is required to use async. So implement it separately as a method
void setUser() async {
user = await UtilSharedPreferencs();
}
void initState(){
super.initState();
setUser();
}
This method will call the init function inside the stateless widget.
But how to emulate the call to dispose function?
var widget = StatelessWidgetExample();
await tester.pumpWidget(widget);
I also tried to emulate the removal from the tree.
await tester.pumpWidget(widget);
await tester.pumpWidget(Container());
but it didn't work
Did it like this
var key2 = Key('a');
var testStateful = _TestStateful(
key: key2,
child: TestInitDispose(),
);
await tester.pumpWidget(testStateful);
/// will call init
var state = tester.firstState<__TestStatefulState>(find.byKey(key2));
state.remove();
await tester.pump();
/// will call dispose
});
...
class _TestStateful extends StatefulWidget {
final Widget child;
const _TestStateful({Key? key, required this.child}) : super(key: key);
#override
__TestStatefulState createState() => __TestStatefulState();
}
class __TestStatefulState extends State<_TestStateful> {
bool showChild = true;
void remove() {
setState(() {
showChild = false;
});
}
#override
Widget build(BuildContext context) {
return showChild ? widget.child : Container();
}
}
You could use a StreamBuilder and replace YourWidget with another widget, then the dispose method for YourWidget is called.
void main() {
late StreamController<Widget> widgetStreamController;
setUp(() async {
widgetStreamController = StreamController<Widget>();
});
tearDown(() async {
await widgetStreamController.close();
});
Widget buildApp() {
return MaterialApp(
home: StreamBuilder<Widget>(
stream: widgetStreamController.stream,
builder: (context, snapshot) {
return snapshot.data ?? Container();
},
),
);
}
testWidgets('dispose widget', (tester) async {
await tester.pumpWidget(buildApp());
await tester.pumpAndSettle();
widgetStreamController.add(YourWidget());
await tester.pumpAndSettle();
// todo: check here if YourWidget is displayed
widgetStreamController.add(AnotherWidget());
await tester.pumpAndSettle();
// todo: check here if dispose was called
});
}
This worked for me and is relatively simple.
var widget = StatelessWidgetExample();
await tester.pumpWidget(widget);
await tester.pumpAndSettle();
await tester.pumpWidget(Container());
await tester.pumpAndSettle();
My home page is Displayed Based on the UserType value that I set in SharedPreffrence, for the first time when I run The app it works fine and the home page is displayed Successfully because it knows the values of Usertype from SharedPreference, but when I close the app and run again The value of UserType become Null, due to this my home page is not Displayed because Usertype value is null, I set SharedPreference when the user login for The first time,
As you See in my code First I pass the value of UserType from SharedPreference, and also I Call Get() method in initState(), to Call SharedPrefrence.
Here is how I set SharedPreference when the user Login for the first time.
Future loginWithEmailAndPasswords(String email, String password,BuildContext context) async {
try {
UserCredential register = await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
User registredUser = register.user;
final customSnapshots= await customSnapshot.doc(registredUser.uid).get();
if(customSnapshots.exists){
SharedPreferences preferences= await SharedPreferences.getInstance() ;
preferences.setString("UserType", customSnapshots.data()['Account type'].toString());
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context)=>homecontroller(
controllUserType:preferences.getString('UserType'),
userid: customSnapshots.data()['userId'].toString(),))); }
}
Here is My HomeController Page.
class homecontroller extends StatelessWidget {
final String controllUserType;
final String userid;
const homecontroller({Key key,#required this.controllUserType,#required this.userid}):super(key: key);
#override
Widget build(BuildContext context) {
Authservice auth
final Authservice auth=Provider.of(context).auth;
return StreamBuilder(
stream:auth.authStateChanges,
builder: (context,AsyncSnapshot<String>snapshot){
if(snapshot.connectionState==ConnectionState.active){
final bool SignedIn=snapshot.hasData;
return SignedIn?HomePage(UserType:controllUserType,userID: userid,):firstview();
}else{
return CircularProgressIndicator();
}
},
);
}
}
Here is My home page
class HomePage extends StatefulWidget {
final String UserType;
final String userID;
const HomePage({Key key,#required this.UserType,#required this.userID}):super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool isSignedIn= false;
String owneruerID;
dynamic uploadusertypes;
List<Widget>_children;
void initState(){
super.initState();
uploadusertypes= widget.UserType; //this value is passed from SharedPreference when the user login for the first time
owneruerID = widget.userID;//this value is passed from SharedPreference when the user login for the first time
GetData(); // here is Where i Call SharedPreference Method
_children=[
TimeLinePage(),
SearchPage(), //search(),
UploadPage(UserSID:owneruerID,uploadusertypes:uploadusertypes),
NotificationsPage(),
ProfilePage(userProfileID:widget.userID),
];
if(FirebaseAuth.instance.currentUser!=null){
setState(() {
isSignedIn= true;
});
}else{
setState(() {
isSignedIn= false;
});
}
}
#override
Widget build(BuildContext context) {
if(isSignedIn){
if(widget.UserType== 'Customer')
{
return Scaffold(
body: WillPopScope(
onWillPop: onwillpops,
child: buildHomeScreen()));
}
}
void GetData()async {
SharedPreferences preferences= await SharedPreferences.getInstance();
setState(() {
widget.UserType=preferences.getString('UserType');
});
}
}
I think first you should call GetData() before the pass shared preference values to variables ( EX - UserType). Then you could get the UserType value and then could pass that value to uploadusertypes.
Try the following code.
class HomePage extends StatefulWidget {
final String UserType;
final String userID;
const HomePage({Key key,#required this.UserType,#required this.userID}):super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool isSignedIn= false;
String owneruerID;
dynamic uploadusertypes;
List<Widget>_children;
void initState(){
super.initState();
GetData(); // you should call this one first.
uploadusertypes= widget.UserType; //this value is passed from SharedPreference when the user login for the first time
owneruerID = widget.userID;//this value is passed from SharedPreference when the user login for the first time
_children=[
TimeLinePage(),
SearchPage(), //search(),
UploadPage(UserSID:owneruerID,uploadusertypes:uploadusertypes),
NotificationsPage(),
ProfilePage(userProfileID:widget.userID),
];
if(FirebaseAuth.instance.currentUser!=null){
setState(() {
isSignedIn= true;
});
}else{
setState(() {
isSignedIn= false;
});
}
}
#override
Widget build(BuildContext context) {
if(isSignedIn){
if(widget.UserType== 'Customer')
{
return Scaffold(
body: WillPopScope(
onWillPop: onwillpops,
child: buildHomeScreen()));
}
}
void GetData()async {
SharedPreferences preferences= await SharedPreferences.getInstance();
setState(() {
widget.UserType=preferences.getString('UserType');
});
}
}
Hope this will solve your problem.
what I am Doing is Just I Remove both uploadusertypes and owneruerID from InitState() and I Declared Them Inside Getdata(), it works but i don't know weather it have effect on my code or not,
uploadusertypes= widget.UserType;
owneruerID = widget.userID;
Here is what I am doing
void initState(){
GetData();
super.initState();
_children=[
TimeLinePage(),
SearchPage(), //search(),
UploadPage(UserSID:owneruerID,uploadusertypes:uploadusertypes),
NotificationsPage(),
ProfilePage(userProfileID:widget.userID),
];
_agentchildren=[
TimeLinePage(),
SearchPage(), //search(),
NotificationsPage(),
ProfilePage(userProfileID:owneruerID),
];
if(FirebaseAuth.instance.currentUser!=null){
setState(() {
isSignedIn= true;
});
}else{
setState(() {
isSignedIn= false;
});
}
uploadusertypes= widget.UserType;
owneruerID = widget.userID;
}
void GetData()async {
SharedPreferences preferences= await SharedPreferences.getInstance();
setState(() {
uploadusertypes=preferences.get('UserType');
widget.UserType=preferences.get('UserType');
});
}
I've the below code that is working fine, reading the csv data from url and printing the output:
import 'package:flutter/material.dart';
import 'dart:convert';
import 'dart:io';
import 'dart:async';
import 'package:csv/csv.dart';
void fetchUserData() async {
final request = await HttpClient().getUrl(Uri.parse(
'https://docs.google.com/spreadsheets/d/e/2PACX-1vQvf9tp4-fETDJbC-HRmRKvVFAXEAGO4lrYPpVeiYkB6nqqXdSs3CjX0eBMvjIoEeX9_qU6K2RWmzVk/pub?gid=0&single=true&output=csv'));
final response = await request.close();
List<List<dynamic>> rowsAsListOfValues;
await for (final csvString in response.transform(const Utf8Decoder())) {
rowsAsListOfValues =
const CsvToListConverter().convert(csvString);
}
print(rowsAsListOfValues);
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
fetchUserData();
}
#override
Widget build(BuildContext context) { // ... // }
}
Instead of getting the output printed, I need it to be returned into a variable, which I can display in y widget, I tried to do it as below:
Future<List<List<dynamic>>> fetchUserData() async { /// change
final request = await HttpClient().getUrl(Uri.parse(
'https://docs.google.com/spreadsheets/d/e/2PACX-1vQvf9tp4-fETDJbC-HRmRKvVFAXEAGO4lrYPpVeiYkB6nqqXdSs3CjX0eBMvjIoEeX9_qU6K2RWmzVk/pub?gid=0&single=true&output=csv'));
final response = await request.close();
List<List<dynamic>> rowsAsListOfValues;
await for (final csvString in response.transform(const Utf8Decoder())) {
rowsAsListOfValues =
const CsvToListConverter().convert(csvString);
}
return rowsAsListOfValues; /// change
}
class _MyHomePageState extends State<MyHomePage> {
var rowsAsListOfValues; /// new
#override
void initState() {
super.initState();
rowsAsListOfValues = fetchUserData(); /// new
print(rowsAsListOfValues); /// new
}
#override
Widget build(BuildContext context) { // ... // }
}
But I got the output as I/flutter ( 7505): Instance of 'Future<List<List<dynamic>>>'
How can I fix it?
You need to switch from initState to didChangeDependency in this case. Because you need to await some process and you cant wait in initState. However you can wait like this
#override
void didChangeDependencies() async {
super.didChangeDependencies();
rowsAsListOfValues = await fetchUserData();
super.setState(() {}); // to update widget data
/// new
print(rowsAsListOfValues);
}
And this is the result
I/flutter (24313): [[vranches], [Dammam, 2], [Khobar, 3]]
You can wrap your code with Future.delayed() as given below.
#override
void initState() {
super.initState();
Future.delayed(Duration.zero,()async{
rowsAsListOfValues =await fetchUserData();
setState(() {});
print(rowsAsListOfValues); // this return correct value
});
print(rowsAsListOfValues); // this return null
}
Full Code
import 'package:flutter/material.dart';
import 'dart:convert';
import 'dart:io';
import 'dart:async';
import 'package:csv/csv.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: HomePage(title: 'Flutter Demo Home Page'),
);
}
}
class HomePage extends StatefulWidget {
HomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_AppState createState() => _AppState();
}
Future<List<List<dynamic>>> fetchUserData() async {
final request = await HttpClient().getUrl(Uri.parse(
'https://docs.google.com/spreadsheets/d/e/2PACX-1vQvf9tp4-fETDJbC-HRmRKvVFAXEAGO4lrYPpVeiYkB6nqqXdSs3CjX0eBMvjIoEeX9_qU6K2RWmzVk/pub?gid=0&single=true&output=csv'));
final response = await request.close();
List<List<dynamic>> rowsAsListOfValues;
await for (final csvString in response.transform(const Utf8Decoder())) {
rowsAsListOfValues =
const CsvToListConverter().convert(csvString);
}
return rowsAsListOfValues;
}
class _AppState extends State<HomePage> {
var rowsAsListOfValues;
#override
void initState() {
super.initState();
Future.delayed(Duration.zero, () async {
rowsAsListOfValues = await fetchUserData();
setState(() {});
print(rowsAsListOfValues);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'$rowsAsListOfValues',
),
],
),
),
);
}
}
The initState method is synchronous, and does not support async. I recommend the use of FutureBuilder, but you can also move the code to an async function.
FutureBuilder
import 'package:flutter/material.dart' show
Widget, FutureBuilder, AsyncSnapshot
;
class _MyHomePageState extends State<MyHomePage> {
static Future<void> fetchUserData() {
return Future().delayed(
Duration(seconds: 10),
() => 'loaded'
);
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: Future.wait([
fetchUserData()
]),
builder: (
BuildContext context,
AsyncSnapshot snapshot
) {
if (snapshot.hasData) {
return Text(snapshot.data);
}
return Text('loading...');
}
);
}
}
Async function
#override
void initState () {
super.initState();
(() async {
rowsAsListOfValues = await fetchUserData();
print(rowsAsListOfValues);
})();
}
OR
#override
void initState() {
super.initState();
initLoad();
}
void initLoad() async {
rowsAsListOfValues = await fetchUserData();
print(rowsAsListOfValues);
}
I feel more relaxed when using then() with async functions. You can try this:
fetchUserData().then((value) {
setState(() {
rowsAsListOfValues = value;
});
});
Or you can use await like this.
#override
void initState() async {
super.initState();
rowsAsListOfValues = await fetchUserData();
print(rowsAsListOfValues);
}
I'm working on flutter and I've a problem with Shared preference plugin
this is my code:
import 'package:flutter/material.dart';
import 'package:newsapp/screens/homescreen.dart';
import 'package:newsapp/screens/onboarding.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences prefs = await SharedPreferences.getInstance();
int seen1 = prefs.getInt('seen');
Widget _screen = HomeScreen();
if (seen1 == null || seen1 == 0)
_screen = OnBoarding();
else {
_screen = HomeScreen();
}
}
class NewsApp extends StatelessWidget {
final Widget _screen;
NewsApp(this._screen);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: this._screen,
);
}
}
this is the error messeage:
No preferred FlutterEngine was provided. Creating a new FlutterEngine for this FlutterFragment.
D/FlutterActivityAndFragmentDelegate( 1817): Attaching FlutterEngine to the Activity that owns this Fragment.
D/FlutterView( 1817): Attaching to a FlutterEngine: io.flutter.embedding.engine.FlutterEngine#e2b1eab
We need to add WidgetsFlutterBinding.ensureInitialized(); before getting
await SharedPreferences.getInstance();
Like :
void main() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences prefs = await SharedPreferences.getInstance();
int seen1 = prefs.getInt('seen');
Widget _screen = HomeScreen();
if (seen1 == null || seen1 == 0)
_screen = OnBoarding();
else {
_screen = HomeScreen();
}
}
you have to end your main() method with runApp(NewsApp());
move your logic code from main() method to NewsApp widget.
convert your NewsApp widget to StatefulWidget to fitch your data from SharedPreferences.
like this :
import 'package:flutter/material.dart';
import 'package:newsapp/screens/homescreen.dart';
import 'package:newsapp/screens/onboarding.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(NewsApp());
}
class NewsApp extends StatefulWidget {
#override
_NewsAppState createState() => _NewsAppState();
}
class _NewsAppState extends State<NewsApp> {
final Widget _screen = HomeScreen();
#override
void initState() {
super.initState();
Future.delayed(Duration.zero, () async {
SharedPreferences prefs = await SharedPreferences.getInstance();
int seen1 = prefs.getInt('seen');
if (seen1 == null || seen1 == 0)
_screen = OnBoarding();
else {
_screen = HomeScreen();
}
setState(() {});
}
}
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: this._screen,
);
}
}