I created a login form in where i am passing the username to dashboard screen using shared perference, problem is when i logged in from abc name and pass it to dashboard it is displaying correct name, but when i logged in from xyz name it display the abc name on dashboard, it is not reseting the variable value, i run the app again, hot reload each and everything but noting happend.
here is the code of login screen
String getname="";
Future login() async {
Dio dio = new Dio();
var myPrefs = await SharedPreferences.getInstance();
data = {
'username':"abc",
'password': "abc123",
'date': formattedDate
};
await dio.post(localhostUrlLogin,data: json.encode(data),)
.then((onResponse) async {
getname = (onResponse.data['User']['username']);
}).catchError((onerror) {});
await myPrefs.setString('name', getname);
Navigator.push(
context, new MaterialPageRoute(builder: (context) =>dashboard()));
}
dashboard screen code
class dashboard extends StatefulWidget {
#override
_dashboard State createState() => _dashboard State();
}
class _dashboard State extends State<dashboard > {
String getname = "";
_userDetails() async {
SharedPreferences myPrefs = await SharedPreferences.getInstance();
setState(() {
getname = myPrefs.getString('name');
}
void initState() {
super.initState();
_userDetails();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[200],
appBar: new MyAppBar(title: Text("Home")),
drawer: NavigationDrawerWidget(),
body: SingleChildScrollView(
child: Column(children: [
SizedBox(height: 10),
Center(
child: Text(
"Hello, " + getname,
style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold),
),
),
}
please help how to fix it.
Related
I am writing a code to check if the user have the correct version of my app installed otherwise redirect him on the Playstore to update the app.
I am using firebase_remote_config to store a variable to keep tract of the minimum version I want the users to use.
Package_info to get information about users app.
url_launcher to redirect to playstore from the app
My problem is that my method to check the version is asynchronous and it needs to show the dialog to the user before entering the first screen of the app.
I execute it in initState. But the build method builds the first screen before the end of my asynchronous function and my ShowDialog did not render.
How can I firstly show the result of my asynchronous function before the first build?
here is my code after some updates but not showing the Dialog before Navigating to another screen
class Splashscreen extends StatefulWidget {
#override
_SplashscreenState createState() => _SplashscreenState();
}
class _SplashscreenState extends State<Splashscreen> {
#override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) {
versionCheck(context);
});
}
Future versionCheck(context) async {
//Get Current installed version of app
final PackageInfo info = await PackageInfo.fromPlatform();
double currentVersion =
double.parse(info.version.trim().replaceAll(".", ""));
//Get Latest version info from firebase config
final RemoteConfig remoteConfig = await RemoteConfig.instance;
try {
// Using default duration to force fetching from remote server.
await remoteConfig.fetch(expiration: const Duration(seconds: 10));
await remoteConfig.activateFetched();
remoteConfig.getString('force_update_current_version');
double nVersion = double.parse(remoteConfig
.getString('force_update_current_version')
.trim()
.replaceAll(".", ""));
if(nVersion > currentVersion){
showDialog(
context: context,
builder: (_) => new AlertDialog(
title: new Text("You are not up to date"),
content: new Text("To use the application, you must update it"),
actions: <Widget>[
FlatButton(
child: Text('Go To Store'),
onPressed: () {
_launchURL(PLAY_STORE_URL);
},
)
],
)
);
}
} on FetchThrottledException catch (exception) {
print(exception);
} catch (exception) {
print(
'Unable to fetch remote config. Cached or default values will be used');
}
}
_launchURL(String url) async {
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
#override
Widget build(BuildContext context) {
return ScopedModelDescendant<UserModel>(builder: (context, child, model) {
return new SplashScreen(
seconds: 4,
navigateAfterSeconds:
model.isSignedIn ? HomePage() : SuggestLoginPage(),
image: new Image.asset('assets/images/logo.png', fit: BoxFit.cover,),
backgroundColor: Color(0xff131921),
styleTextUnderTheLoader: new TextStyle( ),
photoSize: 100.0,
loaderColor: Colors.white
);
});
}
}
Create a splash screen when the app running first time show your logo or a loader.
class SplashScreen extends StatefulWidget {
#override
_SplashScreenState createState() => _SplashScreenState();
}
cclass _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) {
checkVersion();
});
}
Future checkVersion({String payload}) async {
var upToDate = false;
if (upToDate)
print('Navigate to Home');
else
return showDialog(
context: context,
builder: (_) => new AlertDialog(
title: new Text("You are not up to date"),
content: new Text("To use the application, you must update it"),
actions: <Widget>[
FlatButton(
child: Text('Go To Store'),
onPressed: () {
//navigate to store
},
)
],
)
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Stackowerflow",
style: TextStyle(
fontFamily: "Josefin",
fontSize: 55,
fontWeight: FontWeight.bold,
color: Colors.grey,
),
),
Image.asset('assets/images/splash.gif',width: 500,
height: 500,),
],
),
),
);
}
Don't forget to run this splash screen into main.dart
Now you can add your function into checkVersion and if up to date then return home or whatever u want to redirect but if it is not then redirect to the store
I am trying to display a pop-up dialog like (+20) based on the value coming from server. I have a variable name ageRestriction which is getting the value from server. And I want to display pop-up based on the value of this variable. (Eg: If ageRestriction has the value of "18" the pop-up will be displayed only once then later on if the value will change to "20" the pop-up will be displayed once again, so these values will be stored somewhere and the pop-up will not be displayed if the same value comes again)
I have tried to do it with shared preferences unfortunately it did not work:
// initializing shared pref
#override
void initState() async{
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences prefs = await SharedPreferences.getInstance();
widget.ageRestriction = await prefs.getString("ageRestriction");
await prefs.setString("ageRestriction", widget.ageRestriction);
}
// displaying pop-up dialog
widget.ageRestriction.toString() == widget.ageRestriction ? null :
AwesomeDialog(
popContext: false,
context: context,
dialogType: DialogType.WARNING,
animType: AnimType.TOPSLIDE,
title: "${widget.ageRestriction} Warning",
desc: "We only sell this product to persons who are ${widget.ageRestriction} years old. Age will be verified upon delivery.",
btnOkText: "Continue",
btnOkOnPress: () async{
widget.onPressed();
Navigator.of(context).pop();
},
btnCancelOnPress: () {
Navigator.of(context).pop();
},
btnCancelText: S.current.cancel,
btnOkColor: Theme.of(context).accentColor,
btnCancelColor: Color(0xFF084457).withOpacity(0.9),
).show();
ageRestrict();
}
Store widget.ageRestriction in a variable in the state of the widget and then check in didUpdateWidget whether the value changed and if it did show the popup
Simple Demo App to show age restriction :
Future<String> getAgeRestrictionFromServer() async {
// write your own logic
await Future.delayed(Duration(seconds: 2));
return "22";
}
enum RequestState { LOADING, SUCCESS, ERROR }
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>{
// request status
RequestState requestState = RequestState.LOADING;
// disable multiple clickes
bool retryButtonEnabled = true;
#override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
getData();
});
}
showAgeRestrictionDialog(String age) {
return showDialog(
context: context,
child: AlertDialog(
title: Text('Age policy changed!'),
content: Text('New Age: $age'),
),
);
}
void changeRequestState(RequestState newRequestState) {
if (mounted) {
setState(() {
requestState = newRequestState;
});
}
}
Future<void> getData() async {
changeRequestState(RequestState.LOADING);
try {
// get age from server
var newAgeRestriction = await getAgeRestrictionFromServer();
// get age stored locally
SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
var previousAgeRestriction =
sharedPreferences.getString('ageRestriction');
print('$previousAgeRestriction, $newAgeRestriction');
int.parse(newAgeRestriction);
if (mounted) {
// compare previous and new age
if (previousAgeRestriction != newAgeRestriction) {
// save new age
await sharedPreferences.setString(
'ageRestriction', newAgeRestriction);
// show dialog because age changed
showAgeRestrictionDialog(newAgeRestriction);
}
}
retryButtonEnabled = true;
changeRequestState(RequestState.SUCCESS);
} catch (e) {
print(e);
retryButtonEnabled = true;
changeRequestState(RequestState.ERROR);
}
}
#override
Widget build(BuildContext context) {
var child;
if (requestState == RequestState.LOADING) {
child = Center(
child: CircularProgressIndicator(),
);
} else if (requestState == RequestState.SUCCESS) {
child = Center(
child: Text('Got data from server!'),
);
} else {
child = Center(
child: FlatButton(
color: Colors.blue,
onPressed: retryButtonEnabled
? () {
setState(() {
retryButtonEnabled = false;
});
getData();
}
: null,
child: Text('Retry')),
);
}
return Scaffold(
appBar: AppBar(
title: Text('Demo App'),
),
body: child,
);
}
}
More info about state management here
I was create SharedPreferences to save user loading in logon page. Then data of user will be save in SharedPreferences and move to main page. But my problem now in main page I need use this variable in different places in main page. But I cant do that.
I need to make variable of logindata can use in each places in main page I try to use in drawer to make logout. No I get error as:
Undefined name 'logindata'.
this is my code:
void initial() async {
logindata = await SharedPreferences.getInstance();
setState(() {
username = logindata.getString('username');
return username;
});
}
my full code:
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'addnewtopics.dart';
import 'DetilesOfMainPage.dart';
import 'loginpage.dart';
class MyApp extends StatelessWidget {
final String email;
MyApp({Key key, #required this.email}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('JSON ListView')
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Drawer Header'),
decoration: BoxDecoration(
color: Colors.blue,
),
),
ListTile(
title: Text('Item 1'),
onTap: () {
logindata.setBool('login', true);// here I need to use It ========================
Navigator.pushReplacement(context,
new MaterialPageRoute(builder: (context) => LoginUser()));
Navigator.pop(context);
},
),
ListTile(
title: Text('Item 2'),
onTap: () {
// Navigator.pop(context);
},
),
],
),
),
body: JsonImageList(),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => UploadImageDemo()
),);
},
child: Icon(Icons.add),
),
));
}
}
class Flowerdata {
int id;
String flowerName;
String flowerImageURL;
Flowerdata({
this.id,
this.flowerName,
this.flowerImageURL
});
factory Flowerdata.fromJson(Map<String, dynamic> json) {
return Flowerdata(
id: json['id'],
flowerName: json['nametopics'],
flowerImageURL: json['image']
);
}
}
class JsonImageList extends StatefulWidget {
JsonImageListWidget createState() => JsonImageListWidget();
}
class JsonImageListWidget extends State {
SharedPreferences logindata;
String username;
#override
void initState() {
// TODO: implement initState
super.initState();
initial();
}
void initial() async {
logindata = await SharedPreferences.getInstance();
setState(() {
username = logindata.getString('username');
return username;
});
}
final String apiURL = 'http://xxxxxxxxx/getFlowersList.php';
Future<List<Flowerdata>> fetchFlowers() async {
var response = await http.get(apiURL);
if (response.statusCode == 200) {
final items = json.decode(response.body).cast<Map<String, dynamic>>();
List<Flowerdata> listOfFruits = items.map<Flowerdata>((json) {
return Flowerdata.fromJson(json);
}).toList();
return listOfFruits;
}
else {
throw Exception('Failed to load data from Server.');
}
}
getItemAndNavigate(String item, BuildContext context){
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(itemHolder : item)
)
);
}
#override
Widget build(BuildContext context) {
return FutureBuilder<List<Flowerdata>>(
future: fetchFlowers(),
builder: (context, snapshot) {
if (!snapshot.hasData) return Center(
child: CircularProgressIndicator()
);
return ListView(
children: snapshot.data
.map((data) => Column(children: <Widget>[
GestureDetector(
onTap: ()=>{
getItemAndNavigate(data.flowerName, context)
},
child: Row(
children: [
Container(
width: 200,
height: 100,
margin: EdgeInsets.fromLTRB(10, 0, 10, 0),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child:
Image.network(data.flowerImageURL,
width: 200, height: 100, fit: BoxFit.cover,))),
Flexible(child:
Text(data.flowerName,
style: TextStyle(fontSize: 18)))
]),),
Divider(color: Colors.black),
],))
.toList(),
);
},
);
}
}
Anyone know how can make that?
You need var keyword, in your case you can directly use
var logindata = await SharedPreferences.getInstance();
You do not need to make it global, because SharedPreferences.getInstance() is Singleton
Every time you use var logindata = await SharedPreferences.getInstance(); will get the same instance
Also there is no performance issue when you call getInstance(), because it's cached, you can see source code snippet below
class SharedPreferences {
SharedPreferences._(this._preferenceCache);
...
static Future<SharedPreferences> getInstance() async {
if (_completer == null) {
_completer = Completer<SharedPreferences>();
try {
final Map<String, Object> preferencesMap =
await _getSharedPreferencesMap();
_completer.complete(SharedPreferences._(preferencesMap));
} on Exception catch (e) {
// If there's an error, explicitly return the future with an error.
// then set the completer to null so we can retry.
_completer.completeError(e);
final Future<SharedPreferences> sharedPrefsFuture = _completer.future;
_completer = null;
return sharedPrefsFuture;
}
}
return _completer.future;
When you declare a String outside of class and does not contain _ before variable name like _localString it become global
String globalString = ""; //global, import can be seen
String _localString = ""; //local and can only be seen in this file, import can not seen
void main() async{
var logindata = await SharedPreferences.getInstance();
runApp(MyApp());
}
You simply need to put your variable outside of any class or method. An example is to create a file globals.dart then put all your globals in it and import the file when you need.
Example
// globals.dart
String globalString;
int globalInt;
bool globalBool;
// in any other file
import 'globals.dart' as globals;
globals.globalString = "Global String";
I am new to flutter and I am having the following problem.
I am trying to use the progressDialog in in a listview, I make the query to my database, I extract the list and pass it to the listview, I am trying to use the progressDialog so when I start loading the list it will run and tell the user to wait, and when I finish loading the list then the progressDialog is hidden, so far it works for me by bringing me the list and the progressDialog is executed saying to wait, but when I put the progressDialog.hide where the loading of the list ends I this is not accepting that line of code (progressDialog .hidde)
image:
enter image description here
import 'dart:convert';
import 'package:fluterproyecto1/Modulos/DetalleUser.dart';
import 'package:flutter/material.dart';
import 'package:progress_dialog/progress_dialog.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
String username2 = '';
String profesion = '';
String name = '';
class MemberPage extends StatefulWidget {
MemberPage({Key key}) : super(key: key);
#override
_MemberPageState createState() => _MemberPageState();
}
class _MemberPageState extends State<MemberPage> {
Map data;
List userData;
ProgressDialog progressDialog;
String name = '';
Future getData() async {
http.Response response =
await http.get("http://masciudad.com.co/flutter/getdata.php");
data = json.decode(response.body);
//setState(() {
//progressDialog.show();
userData = data["data"];
//progressDialog.hide();
//});
}
#override
void initState() {
super.initState();
getData();
}
#override
Widget build(BuildContext context) {
progressDialog = ProgressDialog(context, type: ProgressDialogType.Normal);
progressDialog.style(message: 'Por favor espere...');
progressDialog.show();
setState(() {
obtenerPreferencias();
});
return Scaffold(
appBar: AppBar(
title: Text("Bienvenido $username2"),
),
body: ListView.builder(
itemCount: userData == null ? 0 : userData.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
children: <Widget>[
Image.asset(
"assets/128.jpg",
width: 30.0,
height: 30.0,
),
//CircleAvatar(
///cuando la imagen es de interntet
//backgroundImage: NetworkImage(
// "https://s3.amazonaws.com/uifaces/faces/twitter/follettkyle/128.jpg"),
//),
Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
"${userData[index]["username"]} - ${userData[index]["profesion"]}",
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w700,
),
),
)
],
),
),
onTap: () => Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) =>
new DetalleUser(name: userData[index]["username"]))),
);
},
),
//Navigator.of(context).push(MaterialPageRoute(
// builder: (BuildContext context) => MyHomePage()));
//Navigator.pushReplacementNamed(context, "/MyHomePage");
);
}
Future obtenerPreferencias() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
setState(() {
username2 = preferences.get("username2") ?? "";
profesion = preferences.get("profesion") ?? "";
});
}
Future destruirPreferencias() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
preferences.clear();
}
}
You can copy paste run full code below
You can use addPostFrameCallback and put logical in another function getRelatedData()
code snippet
void getRelatedData() async {
progressDialog = ProgressDialog(context, type: ProgressDialogType.Normal);
progressDialog.style(message: 'Por favor espere...');
progressDialog.show();
await getData();
await obtenerPreferencias();
progressDialog.hide();
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
getRelatedData();
});
}
working demo
full code
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:progress_dialog/progress_dialog.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
String username2 = '';
String profesion = '';
String name = '';
class MemberPage extends StatefulWidget {
MemberPage({Key key}) : super(key: key);
#override
_MemberPageState createState() => _MemberPageState();
}
class _MemberPageState extends State<MemberPage> {
Map data;
List userData;
ProgressDialog progressDialog;
String name = '';
Future getData() async {
http.Response response =
await http.get("http://masciudad.com.co/flutter/getdata.php");
data = json.decode(response.body);
//setState(() {
//progressDialog.show();
userData = data["data"];
print("getData Done");
//progressDialog.hide();
//});
}
void getRelatedData() async {
progressDialog = ProgressDialog(context, type: ProgressDialogType.Normal);
progressDialog.style(message: 'Por favor espere...');
progressDialog.show();
await getData();
await obtenerPreferencias();
progressDialog.hide();
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
getRelatedData();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Bienvenido $username2"),
),
body: ListView.builder(
itemCount: userData == null ? 0 : userData.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
children: <Widget>[
Image.network(
"https://picsum.photos/250?image=9",
width: 30.0,
height: 30.0,
),
//CircleAvatar(
///cuando la imagen es de interntet
//backgroundImage: NetworkImage(
// "https://s3.amazonaws.com/uifaces/faces/twitter/follettkyle/128.jpg"),
//),
Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
"${userData[index]["username"]} - ${userData[index]["profesion"]}",
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w700,
),
),
)
],
),
),
onTap: () {
/*Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) =>
new DetalleUser(name: userData[index]["username"])));*/
});
},
),
//Navigator.of(context).push(MaterialPageRoute(
// builder: (BuildContext context) => MyHomePage()));
//Navigator.pushReplacementNamed(context, "/MyHomePage");
);
}
Future obtenerPreferencias() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
setState(() {
username2 = preferences.get("username2") ?? "";
profesion = preferences.get("profesion") ?? "";
});
}
Future destruirPreferencias() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
preferences.clear();
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MemberPage(),
);
}
}
Dear #Yeimer I would suggest read documentation carefully
https://pub.dev/packages/progress_dialog
Add the Package this
dependencies:
progress_dialog: ^1.2.4
1
Initialize your ProgressDialog object
final ProgressDialog prDialog = ProgressDialog(context);
//For normal dialog
prDialog = ProgressDialog(context,type: ProgressDialogType.Normal, isDismissible: true/false, showLogs: true/false);
2 Showing the progress dialog
await pr.show();
3
Dismissing the progress dialog
prDialog.hide().then((isHidden) {
print(isHidden);
});
// or simply
await prDialog.hide();
I want launch the notification when the background process is triggered,
I tried call the notification class inside BackgroundFetch method but didn't work ,However when i print a debug message it works.
and i want to make this functionality isolated from the business logic of the app
this is my attempt
1-the notification class:
class LocalNotificationWidget extends StatefulWidget {
#override
_LocalNotificationWidgetState createState() =>
_LocalNotificationWidgetState();
}
class _LocalNotificationWidgetState extends State<LocalNotificationWidget> {
final notifications = FlutterLocalNotificationsPlugin();
#override
void initState() {
super.initState();
final settingsAndroid = AndroidInitializationSettings('app_icon');
final settingsIOS = IOSInitializationSettings(
onDidReceiveLocalNotification: (id, title, body, payload) =>
onSelectNotification(payload));
notifications.initialize(
InitializationSettings(settingsAndroid, settingsIOS),
onSelectNotification: onSelectNotification);
showOngoingNotification(notifications,
title: 'Tite', body: 'hello');
}
Future onSelectNotification(String payload) async => await Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen( payload)),
);
#override
Widget build(BuildContext context) => Container();
Widget title(String text) => Container(
margin: EdgeInsets.symmetric(vertical: 4),
child: Text(
text,
style: Theme.of(context).textTheme.title,
textAlign: TextAlign.center,
),
);
}
2-the background service class:
const EVENTS_KEY = "fetch_events";
/// This "Headless Task" is run when app is terminated.
void backgroundFetchHeadlessTask() async {
print('[BackgroundFetch] Headless event received.');
SharedPreferences prefs = await SharedPreferences.getInstance();
// Read fetch_events from SharedPreferences
List<String> events = [];
String json = prefs.getString(EVENTS_KEY);
if (json != null) {
events = jsonDecode(json).cast<String>();
}
// Add new event.
events.insert(0, new DateTime.now().toString() + ' [Headless]');
// Persist fetch events in SharedPreferences
prefs.setString(EVENTS_KEY, jsonEncode(events));
BackgroundFetch.finish();
}
void main() {
// Enable integration testing with the Flutter Driver extension.
// See https://flutter.io/testing/ for more info.
runApp(new MyApp());
// Register to receive BackgroundFetch events after app is terminated.
// Requires {stopOnTerminate: false, enableHeadless: true}
BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask);
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _enabled = true;
int _status = 0;
List<String> _events = [];
#override
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
// Load persisted fetch events from SharedPreferences
SharedPreferences prefs = await SharedPreferences.getInstance();
String json = prefs.getString(EVENTS_KEY);
if (json != null) {
setState(() {
_events = jsonDecode(json).cast<String>();
});
}
// Configure BackgroundFetch.
BackgroundFetch.configure(BackgroundFetchConfig(
minimumFetchInterval: 15,
stopOnTerminate: false,
enableHeadless: true,
forceReload: false
), _onBackgroundFetch).then((int status) {
LocalNotificationWidget();
print('[BackgroundFetch] SUCCESS: $status');
setState(() {
_status = status;
});
}).catchError((e) {
print('[BackgroundFetch] ERROR: $e');
setState(() {
_status = e;
});
});
// Optionally query the current BackgroundFetch status.
int status = await BackgroundFetch.status;
setState(() {
_status = status;
});
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
}
void _onBackgroundFetch() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
// This is the fetch-event callback.
print('[BackgroundFetch] Event received');
setState(() {
_events.insert(0, new DateTime.now().toString());
});
// Persist fetch events in SharedPreferences
prefs.setString(EVENTS_KEY, jsonEncode(_events));
// IMPORTANT: You must signal completion of your fetch task or the OS can punish your app
// for taking too long in the background.
BackgroundFetch.finish();
}
void _onClickEnable(enabled) {
setState(() {
_enabled = enabled;
});
if (enabled) {
BackgroundFetch.start().then((int status) {
print('[BackgroundFetch] start success: $status');
}).catchError((e) {
print('[BackgroundFetch] start FAILURE: $e');
});
} else {
BackgroundFetch.stop().then((int status) {
print('[BackgroundFetch] stop success: $status');
});
}
}
void _onClickStatus() async {
int status = await BackgroundFetch.status;
print('[BackgroundFetch] status: $status');
setState(() {
_status = status;
});
}
void _onClickClear() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.remove(EVENTS_KEY);
setState(() {
_events = [];
});
}
#override
Widget build(BuildContext context) {
const EMPTY_TEXT = Center(child: Text('Waiting for fetch events. Simulate one.\n [Android] \$ ./scripts/simulate-fetch\n [iOS] XCode->Debug->Simulate Background Fetch'));
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: const Text('BackgroundFetch Example', style: TextStyle(color: Colors.black)),
backgroundColor: Colors.amberAccent,
brightness: Brightness.light,
actions: <Widget>[
Switch(value: _enabled, onChanged: _onClickEnable),
]
),
body: (_events.isEmpty) ? EMPTY_TEXT : Container(
child: new ListView.builder(
itemCount: _events.length,
itemBuilder: (BuildContext context, int index) {
String timestamp = _events[index];
return InputDecorator(
decoration: InputDecoration(
contentPadding: EdgeInsets.only(left: 5.0, top: 5.0, bottom: 5.0),
labelStyle: TextStyle(color: Colors.blue, fontSize: 20.0),
labelText: "[background fetch event]"
),
child: new Text(timestamp, style: TextStyle(color: Colors.black, fontSize: 16.0))
);
}
),
),
bottomNavigationBar: BottomAppBar(
child: Container(
padding: EdgeInsets.only(left: 5.0, right:5.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
RaisedButton(onPressed: _onClickStatus, child: Text('Status: $_status')),
RaisedButton(onPressed: _onClickClear, child: Text('Clear'))
]
)
)
),
),
);
}
}