I want to have the app version number displayed in my settings when the settings page is opened. My issue is that I learned that has to be done asynchronously. How do I get the version number and display it in a Text once it gets it?
My code that returns a future:
Future<String> getVersion() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
return packageInfo.version;
}
I want to display the version here:
ListTile(
title: Text("Version"),
subtitle: Text("1.0.0"), //replace with getVersion()
),
Use package_info to get the version of the app.
make state variable with dummy initialization and in initState make a function call to get the version value and update the state variable which is initialized with the dummy value
My working code here
//initialize dummy value
PackageInfo _packageInfo = new PackageInfo(
appName: 'Unknown',
packageName: 'Unknown',
version: 'Unknown',
buildNumber: 'Unknown',
);
#override
void initState() {
super.initState();
//get package details
_initPackageInfo();
}
Future<Null> _initPackageInfo() async {
final PackageInfo info = await PackageInfo.fromPlatform();
setState(() {
_packageInfo = info;
});
}
Render list tile view as
new ListTile(
title: new Text('${_packageInfo.version}'),
leading: const Icon(
FontAwesomeIcons.codeBranch,
size: 20.0,
),
),
you view will show somthing like icon 1.0.0 which is specified as version in your package.
Hope it helps you. let me know if not
Related
I have several separate requests to my websockets and I wanted to get data from all of these using parametrs. AnimalID in getImageAnimal - parametrs for getting image of fixed animals. here is requests I am trying to make
final hubConnection1 = HubConnectionBuilder()
.withUrl(
'http://10.10:8000/animalHub',
options: httpConnectionOptions,
)
.build();
await hubConnection1.start();
List fixationAnimals = [];
if (hubConnection1.state == HubConnectionState.Connected) {
await hubConnection1
.invoke('GetAnimalFixations')
.then((value) => fixation = value as List)
.then((value1) => hubConnection1.invoke('GetImageAnimal',
args: [imageId ?? '']).then((value2) => imageId == value2));
}
hubConnection1.onclose(({error}) {
logger?.finer(error);
});
print(imageId);
print(fixation);
return fixation;
in GetAnimalFixations I have such field:
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"animalId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "string",
"fixedTimeAnimal": "2022-08-23T07:25:29.898Z",
"imageId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
]
I found out how to do it. Everything i post here is just for testing pupose, just to make sure it is really works. Please do not write like below (as I know it is a bad decision to manipulate FutureBuilder as I did below, but it is ONLY for testing purpose)
I have several requests and one of them are depended on the other one.
GetAnimalFixations (the main request)-> GetImageAnimal (depended one, where I should put arguments imageId from the first request)
STEP 1
Create two separate requests with methods above like that:
Future<List> fetchAnimalFixation() async {
final hubConnection1 = HubConnectionBuilder()
.withUrl(
'http://10.10:8000/animalHub',
options: httpConnectionOptions,
)
.build();
await hubConnection1.start();
List fixationAnimals = [];
if (hubConnection1.state == HubConnectionState.Connected) {
await hubConnection1
.invoke('GetAnimalFixations')
.then((value) => fixation = value as List);
}
hubConnection1.onclose(({error}) {
logger?.finer(error);
});
print(fixation);
return fixation;
}
Second request:
Future<String> fetchAnimalImage(String imageId) async {
final hubConnection1 = HubConnectionBuilder()
.withUrl(
'http://10.10:8000/animalHub',
options: httpConnectionOptions,
)
.build();
await hubConnection1.start();
String image = [];
if (hubConnection1.state == HubConnectionState.Connected) {
await hubConnection1
.invoke('GetImageAnimal', args[imageId])
.then((value) => image= value as String);
}
hubConnection1.onclose(({error}) {
logger?.finer(error);
});
print(image);
return image;
}
STEP 2
First preparation is over, now we a go into the second steps to achive our goal:
You need to create the screen where all data will be displayed. For my testing purpose (again: ONLY for testing and small piece of data) I use future builder:
body: FutureBuilder<List>(
future: AllFixation().fetchAnimalFixation(),
builder: (context, snapshot) {
return ListView.builder(
itemCount: 2,
itemBuilder: (context, index) {
return Card(
child: Column(
children: [
Text(snapshot.data?[index]['name'].toString() ?? ''),
FutureBuilder<String?>(
future: AllFixation().fetchAnimalImage(
snapshot.data?[index]['imageId'].toString() ??
''),
builder: (context, snapshotImage) {
Uint8List bytes =
base64.decode(snapshotImage.data ?? '');
return Image.memory(bytes);
}),
Voila! All is ready. You can use and enjoy the picture and the name of the animal. All this via websocket and using signalR package. I higly recommend to use some state management and think that you are going to do next, because it is the solid piece of code and if someday it will need to change, it turn out to be the biggest pain.
I am trying to make my previously static app, dynamic by making calls to the backend server.
This is what my service looks like
String _url = "http://localhost:19013/template/listAll";
Future<List<TemplateInfoModel>> fetchTemplates() async {
final response =
await http.get(_url);
print(response.statusCode);
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON
Iterable l = json.decode(response.body);
List<TemplateInfoModel> templates = l.map((i) => TemplateInfoModel.fromJson(i)).toList();
return templates;
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
And my model looks like this :
class TemplateInfoModel {
final String templateId;
final String messageTag;
final String message;
final String messageType;
TemplateInfoModel({this.templateId, this.messageTag, this.message,
this.messageType});
factory TemplateInfoModel.fromJson(Map<String, dynamic> json) {
return TemplateInfoModel ( templateId: json['templateId'], messageTag : json['messsageTag'], message : json ['message'] , messageType : json['messageType']);
}
}
I have a utils method within which I am capturing the http request/response data; which would then use that to create a DropDown Widget ( or display it within a text)
My earlier dummy data was a list; I am wondering how best I can convert this Future> to List
class SMSTemplatingEngine {
var _SMSTemplate; //TODO this becomes a Future<List<TemplateInfoModels>>
// TemplateInfoService _templateInfoService;
SMSTemplatingEngine(){
_SMSTemplate=fetchTemplates();
}
// var _SMSTemplate = {
// 'Reminder':
// 'Reminder : We’re excited to launch a new feature on our platform that will revolutionize your Facebook marketing and triple your ROI. Visit url.com to learn more',
// 'New Message':
// 'New Message: We’re excited to launch a new feature on our platform that will revolutionize your Facebook marketing and triple your ROI. Visit url.com to learn more',
// 'Connecting Again':
// 'Connecting Again : We’re excited to launch a new feature on our platform that will revolutionize your Facebook marketing and triple your ROI. Visit url.com to learn more',
// };
List<String> getKeys(){
List<String> smsKeys = new List();
for ( var key in _SMSTemplate.keys)
smsKeys.add(key);
return smsKeys;
}
String getValuePerKey(String key){
return _SMSTemplate['${key}'];
}
}
P.S. I have looked at some posts but I was completely bowled over since I am a Flutter Newbie.
Is there an easier way for this to happen.
Thanks,
The widget which would display the content from the http call
var templateMessagesDropDown = new DropdownButton<String>(
onChanged: (String newValue) {
setState(() {
templateMsgKey = newValue;
print("Selcted : ${templateMsgKey.toString()} ");
});
},
// value: _defaultTemplateValue,
style: textStyle,
//elevation: 1,
hint: Text("Please choose a template"),
isExpanded: true,
//
items: smsEngine.getKeys().map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
I am wondering how best I can convert this Future to List
Future<List<TemplateInfoModel>> fetchTemplates() should return a List that you're expecting. You may want to consider either using FutureBuilder or StreamBuilder to update the UI elements on your screen. Or if you're not keen on using either of those, you can just call the Future and update the List on your current screen.
List<TemplateInfoModel> _listTemplateInfoModel = [];
...
fetchTemplates().then((value){
setState((){
_listTemplateInfoModel = value;
});
});
Flutter beginner here; I'm getting this error: type 'Future<dynamic>' is not a subtype of type 'String'
Function that fetch download url from firebase storage
_getImageUrl(var fileName) async {
print(fileName);
final StorageReference ref = FirebaseStorage.instance.ref().child('categories/' + fileName);
Uri downloadUrl = await ref.getDownloadURL();
var url = downloadUrl.toString();
return url;
}
where I called the function
child: Image(
image: AdvancedNetworkImage(
_getImageUrl(menuItem['image'].toString()),
timeoutDuration: Duration(minutes: 1),
useDiskCache: true,
cacheRule: CacheRule(maxAge: const Duration(days: 7)),
),
height: mediaQuery.size.width * 0.22,
width: mediaQuery.size.width * 0.22,
),
You cannot use async/await when returning from a build method (or a builder closure). Any time you have async when building the widget tree, it's best to use a FutureBuilder:
child: FutureBuilder<String>(
future: _getImageUrl(menuItem['image'].toString()),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Image(
image: AdvancedNetworkImage(
snapshot.data,
timeoutDuration: Duration(minutes: 1),
useDiskCache: true,
cacheRule: CacheRule(maxAge: const Duration(days: 7)),
),
height: mediaQuery.size.width * 0.22,
width: mediaQuery.size.width * 0.22,
);
}
return CircularProgressIndicator();
}
),
Alternatively, you could use a StatefulWidget for this but it's much more boilerplate. There are more details and a live sample at https://flutterigniter.com/build-widget-with-async-method-call/ if you're interested.
Firstly Flutter returns a Future<type> when the asynchronous functions is not completed, when it is completes it will return data of the type type. The mistake you are making is calling an asynchronous from a synchronous function. Although dart will execute the functions asynchronously (You can validate this by printing the url from the asynchronous function). But the place you are calling it from is not waiting for the asynchronous function to finish.
I hope it is clear now. For more clarity visit Asynchronous Programming
Your _getImageUrl shoul return a Future<String> .
I want to save user preferences using Flutter's SharedPreference. But the registered preferences are ALL null at new start (when app have been closed, not unistalled).
settings.dart :
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class SettingsPage extends StatefulWidget{
#override
_SettingsPageState createState() => new _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage>{
bool _param1IsON;
bool _param2IsON;
bool _param3IsON;
#override
void initState() {
super.initState();
_param1IsON = false;
_param2IsON = false;
_param3IsON = false;
loadPreferences();
}
Future<Null> loadPreferences() async {
SharedPreferences _preferences = await SharedPreferences.getInstance();
if(_preferences.getBool('setted') == null || !_preferences.getBool('setted'))
SharedPreferences.setMockInitialValues({}); // Library fix line
bool param1IsON = _preferences.getBool('param1IsON');
bool param2IsON = _preferences.getBool('param2IsON');
bool param3IsON = _preferences.getBool('param3IsON');
setState(() {
_param1IsON = (param1IsON != null)? param1IsON : _param1IsON;
_param2IsON = (param2IsON != null)? param2IsON : _param2IsON;
_param3IsON = (param3IsON != null)? param3IsON : _param3IsON;
});
_preferences.setBool('setted', true);
}
Future<Null> setBoolSettings(String key, bool value) async {
switch(key){
case 'param1IsON':
setState(() {
_param1IsON = value;
});
break;
case 'param2IsON':
setState(() {
_param2IsON = value;
});
break;
case 'param3IsON':
setState(() {
_param3IsON = value;
});
break;
default:
print("Unknown settings '$key'");
}
SharedPreferences _preferences = await SharedPreferences.getInstance();
await _preferences.setBool(key, value);
}
#override
Widget build(BuildContext context) {
return new ListView(
children: <Widget>[
new ListTile(
title: new Text(Param 1'),
trailing: new Switch(value: _param1IsON,
onChanged: (bool newValue) {
setBoolSettings('param1IsON', newValue);
}),
),
new ListTile(
title: new Text('Param 2'),
trailing: new Switch(value: _param2IsON,
onChanged: (bool newValue) {
setBoolSettings('param2IsON', newValue);
}),
),
new ListTile(
title: new Text('Param 3'),
trailing: new Switch(value: _param3IsON,
onChanged:
(bool newValue) {
setBoolSettings('param3IsON', newValue);
}),
),
]
);
}
}
What I get:
At lunch 3 parameters are false. If I turn 'ON' one of them, wait 2s (it is not an async problem), then close the app and Start again... All of my parameters are false.
What I want:
At lunch 3 parameters are false. I turn 'ON' one of them. I close the app. Start again. The previous param I turned 'ON' is still true.
I had the same issue and fixed it in Android/../MainActivity.java by adding at the top:
import io.flutter.plugins.GeneratedPluginRegistrant;
As well as under super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
The problem comes from using
SharedPreferences.setMockInitialValues({});
I got it from Flutter Test MissingPluginException but it seems to clear all the shared preferences.
However if you remove SharedPreferences.setMockInitialValues({}); and you don't have the two lines above in MainActivity.java, you'll get:
MissingPluginException(No implementation found for method getAll on
channel flutter: plugins.flutter.io/shared_preferences)
I hope it helps!
Hi I also faced the same issue. Did so many things. nothing helped .This may help someone.First thing ,followed this url and did the changes
1.https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects
2.Run the command
flutter upgrade
3.Changed the shared preferences plugin in pubspec.yaml file
shared_preferences: ">=0.5.10 <2.0.0"
4.Deleted the pub cache from installed flutter location
C:\Users\myuser\AppData\Local\Pub\Cache\hosted\pub.dartlang.org
5.flutter build apk --debug
6.flutter build apk --profile
7.flutter run --release (if I run directly this command its throwing error like debug.jar not found , so I ran command 5 and 6 )
Command 7 is for - To Verify whether its working perfectly in release mode.
Finally I tried to generate app build without shrinking the code. then it worked
flutter build apk --no-shrink
flutter build appbundle --no-shrink
This question already has answers here:
Using Navigator.popUntil and route without fixed name
(2 answers)
Closed 3 years ago.
I have screens A->B->C->D
In B, C, D screens there is a button that should take you to screen A keeping it's state (thus pushNamedAndRemoveUntil isn't appropriate here).
I want to use popUntil, that's how I do it, based on docs:
Navigator.popUntil(context, ModalRoute.withName(ScreenName.mainScreen));
I get an error:
Bad state: Future already completed
Here is my main:
void main() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
final pages = {
ScreenName.mainScreen: (settings) => MaterialPageRoute(
builder: (context) => MainScreen(), settings: settings),
};
var configureApp = AppConfig(
appName: 'TaskerMate',
flavorName: FLAVOR_NAME.PROD,
child: AppModelProvider(
model: AppModel(),
child: MaterialApp(
theme: TMTheme().get(),
home: SplashScreen(),
onGenerateRoute: (settings) {
pages[settings.name](settings);
},
routes: {ScreenName.mainScreen: (context) => MainScreen()},
),
),
);
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((LogRecord rec) {
print('${rec.level.name}: ${rec.time}: ${rec.message}');
});
runApp(configureApp);
}
ScreenName.mainScreen -> static final String mainScreen = '/main';
Took me ages to find the answer, but if anyone finds themselves stuck with this problem, Rémi Rousselet's answer to another question is what solved it for me:
Navigator.pushReplacement(
context,
MaterialPageRoute(settings: RouteSettings(name: "Foo")),
);
Add settings to MaterialPageRoute with name, then call popUntil like so:
Navigator.popUntil(context, ModalRoute.withName("Foo"))