only static members can be accessed in initializers - dart

I have an int value passed from the first screen to the second screen and I can have my value without any problem...My problem is that I want to add my recieved value to astring to complete and start working with my api which reqires to add the imported value of the first screen...Iam trapped between the recieved int which I can not change it to a static, and the api string which reqires the added value to be a static
the second Screen:
class CatsNews extends StatefulWidget {
#override
_CatsNewsState createState() => new _CatsNewsState();
}
class _CatsNewsState extends State<CatsNews> {
int _id ;
String root = "http://api.0mr.net";
String url = "/api/GetCategoryById/$_id";
#override
List data;
Future<String> getDrawerItem() async {
var response = await http.get(
Uri.encodeFull(url), headers: {"Accept": "application/json"});
setState(() {
var respondbody = json.decode(response.body);
data = respondbody["StoriesCategory"];
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Cat Screen'),
),
body: Center()
I get the value through the shared preferences and it works fine if it was inside Widget build(BuildContext context)
#override
void initState() {
getIntPrefrence().then(updatId);
super.initState();
this.getDrawerItem();
}
void updatId(int id) {
setState(() {
this._id = id;
});
}
}
UPDATE:
I have added the Srting url to the initstate() and the code is as foloows:
class _CatsNewsState extends State<CatsNews> {
#override
List data;
int _id ;
var response;
String root = "http://-api.0mr.net";
String url ;
Future<String> getDrawerItem() async {
response = await http
.get(Uri.encodeFull(url), headers: {"Accept": "application/json"});
setState(() {
var respondbody = json.decode(response.body);
data = respondbody["StoriesCategory"];
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('$_id'),
),
body: Center()
}
Future<int> getIntPrefrence() async {
SharedPreferences pref = await SharedPreferences.getInstance();
int id = pref.getInt("id");
return id;
}
#override
void initState() {
super.initState();
getIntPrefrence().then(updatId);
updateurl();
this.getDrawerItem();
//initResponse();
}
updateurl() {
setState(() {
this.url= "$root/api/CategoryId/$_id";
});
}
void updatId(int id) {
setState(() {
this._id = id;
});
}
}
the previous issue was solved by adiing the String to the initstate(), but the updated values does not add to the String and deals witht the imported int id as null or zero,however the int id works fine and shows the imported value in any widget inside the Widget build(BuildContext context)

The offending code is probably
var response = await http.get(...
You can't have arbitrary code in a field initializer which is the part after = in above code.
Dart has a strict order in object creation steps.
Field initializers are executed before the constructor initialization list and before the constructor initialization list of super classes.
Only after all the constructor initialization lists of all super classes are executed, the constructor bodies are executed and only from then on is it allowed to access this, the instance of your class being created.
Your code accesses this (implicitly) before that and at this point object initialization isn't done and therefore access to this prohibited to prevent undefined bahavior.
Static members are safe to access, because they don't depend on object initialization. They are ready to use without an instance entirely.
What the error message is telling you is that your initializer is trying to do things that are not possible at this point and you need to move the code somewhere else.
The field initialization code can be moved to the constructor initialization list. This is usually done if the field is supposed to be final,
or to the constructor body, or to a method - as shown below.
var response;
#override
void initState() {
super.initState();
_initResponse();
}
void _initResponse() async {
response = await http.get(
Uri.encodeFull(url), headers: {"Accept": "application/json"});
setState(() {
var respondbody = json.decode(response.body);
data = respondbody["StoriesCategory"];
});
}
Because initState does not allow async we move the code to another method (_initResponse) that we call from initState.

the solution of my problem was adding both the imported value and the String which I want to edit according to it inside the method which will start my api request as follows:
void initState() {
super.initState();
this.getDrawerItem();
}
Future<String> getDrawerItem() async {
int _id ;
String url;
SharedPreferences pref = await
SharedPreferences.getInstance();
_id = (pref.getInt("id")?? 0);
url = "http://gomhuriaonline-api.0mr."
"net/api/GetCategoryById/$_id";
print(url);
response = await http
.get(Uri.encodeFull(url), headers:
{"Accept": "application/json"});
setState(() {
var respondbody = json.decode(response.body);
data = respondbody["StoriesCategory"];
});
}
}

Related

Fetch Data from a Wix Database through http request

I created a database with Wix that has several different types of content like Strings, images, addresses and more.I want to use the information from the WIX database for an app(made with Flutter and Dart); simply portraying the information in a ListView but it seems the data doesnt reach the app.
I created the necessary function on Wix to make the database accessible for third parties and tested it with Postman. When i make the request with this Url (https://daudadmin.editorx.io/acteeventpage/_functions/regions) it works fine and Postman returns the items with all the information as JSON.
Now when i use the Url in my app code; it just returns a blank page. That is the code i use currently:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final url = 'https://daudadmin.editorx.io/acteeventpage/_functions/regions';
var _postsJson = [];
void fetchData() async {
try {
final response = await get(Uri.parse(url));
final jsonData = jsonDecode(response.body) as List;
setState(() {
_postsJson = jsonData;
});
} catch (err) {
//handle error here with error message
}
}
#override
void initState() {
// TODO: implement initState
super.initState();
fetchData();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ListView.builder(
itemCount: _postsJson.length,
itemBuilder: (context, i) {
final post = _postsJson[i];
return Text("Title: ${post["title"]}");
}),
),
);
}
}
This is the line causing your error
final jsonData = jsonDecode(response.body) as List;
The data being returned from the endpoint is a Map and not a List.
{
"items": [
{},
...
],
}
To access the list, try accessing the items property of the map by changing the declaration to
final jsonData = jsonDecode(response.body)["items"] as List;

Flutter integrating Hive database with Riverpod

There is very easy way to use Hive key-value database on StatefulWidgets, for example:
class HookDemo extends StatefulWidget {
#override
_HookDemoState createState() => _HookDemoState();
}
class _HookDemoState extends State<HookDemo> {
Box user;
#override
void initState() {
super.initState();
user = Hive.box<User>('user');
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
final _u = User()
..nameFamily = 'myname'
..mobileNumber = '123456789';
_user!.add(_u);
_u.save();
},
child: Icon(Icons.add),
),
...
);
}
}
here we defined Box user property and inside initState we implemented what's user such as user = Hive.box<User>('user');
after that we can use user without any problem and getting already opened error
now in this current application we used HookWidget and when we want to use Hive we get error as box already opened
main.dart:
Future<void> initHiveDriver() async {
final appDocumentDirectory = await path_provider.getApplicationDocumentsDirectory();
await Hive.initFlutter(appDocumentDirectory.path);
await Hive.openBox<UserAdapter>('user');
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
///...
initHiveDriver();
runApp(
ProviderScope(observers: [
Logger()
],
child: MyApp()),
);
}
how can i create a provider for Hive with Riverpod and use it into HookWidget?
I am using Hive with Riverpod like this.
I am using a named constructor so I can await the openBox call.
final hiveProvider = FutureProvider<HiveDB>((_) => HiveDB.create());
class HiveDB {
var _userBox;
HiveDB._create() {}
static Future<HiveDB> create() async {
final component = HiveDB._create();
await component._init();
return component;
}
_init() async {
Hive.registerAdapter(UserAdapter());
this._userBox = await Hive.openBox<User>('user');
}
storeUser(User user) {
this._userBox.put('user', user);
}
User getUser() {
return this._userBox.get('user');
}
}
Use in a ConsumerWidget:
class SomeWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final provider = ref.watch(hiveProvider).data?.value;
...
}
}

Flutter Text To Speech: Speak several strings subsequently with different SpeechRates

I have a problem with the Flutter Text To Speech package.
When clicking on a FloatingActionButton I would like to speak/play several Strings (with different Speechrates) subsequently. However, when doing so, I can only hear the last string that I have passed onto the function and not the first one.
As you can see in the code below, I have tried to make use of the asynchronus programming (async / await).
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_tts/flutter_tts.dart';
class SpeakerClass extends StatefulWidget{
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _SpeakerClassState();
}
}
class _SpeakerClassState extends State<SpeakerClass>{
String text1 = 'eins';
String text2 = 'zwei';
String text3 = 'drei';
String text4 = 'vier';
String currentTtsString;
double ttsSpeechRate1 = 0.5;
double ttsSpeechRate2 = 1.0;
double currentSpeechRate;
Future playTtsString1() async {
currentTtsString = text1;
currentSpeechRate = ttsSpeechRate1;
await runTextToSpeech(currentTtsString, currentSpeechRate);
return null;
}
Future playTtsString2() async {
currentTtsString = text2;
currentSpeechRate = ttsSpeechRate2;
await runTextToSpeech(currentTtsString, currentSpeechRate);
return null;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FloatingActionButton (
backgroundColor: Colors.blue,
child: Icon(Icons.volume_up, color: Colors.white),
onPressed: () async {
await playTtsString1();
await playTtsString2();
},
)
);
}
}
Future<void> runTextToSpeech(String currentTtsString, double currentSpeechRate) async {
FlutterTts flutterTts;
flutterTts = new FlutterTts();
await flutterTts.setLanguage("en-GB");
await flutterTts.setVolume(1.0);
await flutterTts.setPitch(1.0);
await flutterTts.isLanguageAvailable("en-GB");
await flutterTts.setSpeechRate(currentSpeechRate);
await flutterTts.speak(currentTtsString);
}
When pressing the FloatingActionButton I expect the program to first carry out the function playTtsString1 ("eins" with a speed of 0.5) and afterwards the function playTtsString2 ("zwei" with a speed of 1).
However, somehow I can only hear the program saying "zwei". I guess the program is not waiting for the first function "playTtsString1" to be finished and already carries out the second function "playTtsString2". I would really appreciate any help on this matter!
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_tts/flutter_tts.dart';
class SpeakerClass extends StatefulWidget{
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _SpeakerClassState();
}
}
class _SpeakerClassState extends State<SpeakerClass>{
String text1 = 'eins';
String text2 = 'zwei';
String text3 = 'drei';
String text4 = 'vier';
String currentTtsString;
double ttsSpeechRate1 = 0.5;
double ttsSpeechRate2 = 1.0;
double currentSpeechRate;
FlutterTts flutterTts;
bool bolSpeaking = false;
Future playTtsString1() async {
bolSpeaking = true;
currentTtsString = text1;
currentSpeechRate = ttsSpeechRate1;
await runTextToSpeech(currentTtsString, currentSpeechRate);
return null;
}
Future playTtsString2() async {
bolSpeaking = true;
currentTtsString = text2;
currentSpeechRate = ttsSpeechRate2;
await runTextToSpeech(currentTtsString, currentSpeechRate);
return null;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FloatingActionButton (
backgroundColor: Colors.blue,
child: Icon(Icons.volume_up, color: Colors.white),
onPressed: () async {
// Play String 1
await playTtsString1();
// Check String 1 Finish
new Future.delayed(new Duration(milliseconds: 100), () async {
// loop until bolSpeaking = false
while (bolSpeaking) {
await Thread.sleep(100);
}
// play String 2
await playTtsString2();
}
},
)
);
}
}
Future<void> runTextToSpeech(String currentTtsString, double currentSpeechRate) async {
flutterTts = new FlutterTts();
await flutterTts.setLanguage("en-GB");
await flutterTts.setVolume(1.0);
await flutterTts.setPitch(1.0);
await flutterTts.isLanguageAvailable("en-GB");
await flutterTts.setSpeechRate(currentSpeechRate);
flutterTts.setCompletionHandler(() {
setState(() {
// The following code(s) will be called when the TTS finishes speaking
bolSpeaking = false;
});
});
flutterTts.speak(currentTtsString);
}
This should now work with the latest flutter_tts version.
You simply need to set awaitSpeakCompletion before the speaking happens.
You can update your run method like so:
Future<void> runTextToSpeech(String currentTtsString, double currentSpeechRate) async {
FlutterTts flutterTts;
flutterTts = new FlutterTts();
await flutterTts.awaitSpeakCompletion(true);
await flutterTts.setLanguage("en-GB");
await flutterTts.setVolume(1.0);
await flutterTts.setPitch(1.0);
await flutterTts.isLanguageAvailable("en-GB");
await flutterTts.setSpeechRate(currentSpeechRate);
await flutterTts.speak(currentTtsString);
}

Flutter Shared Preferences Auth FIle

I'm trying to write an auth file, with a list of finals with shared preferences values in it. I could import that auth file in my other files and i could get like the name or email without importing shared preferences in every file.
It would probably look way smoother and cleaner.
I thought something like this would have worked but it didn't
/// ------------Auth------------ ///
final email = getEmail();
getEmail() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString('email');
}
Does anybody have any idea how to do this?
Greetings,
Jente
I assume you want to use the method in multiple files. The problem with your code is that the getEmail method is marked async that means it will have to return a Future. Think about it like this, when you mark a method as async it means it will return something (or finish executing) in the near future. When ? Well you don't know exactly when, so you'll need to get "notified" when the method is "done", that's why you'll use a Future. Something like this:
Future<String> getEmail() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString('email');
}
class ThisIsTheClassWhereYouWantToUseTheFunction {
//let's say you have a method in your class called like the one below (it can be any other name)
void _iNeedTheEmailToPrintIt() {
//this is the way you can call the method in your classes, this class is just an example.
getEmail().then((thisIsTheResult){ // here you "register" to get "notifications" when the getEmail method is done.
print("This is the email $thisIsTheResult");
});
}
}
you can define a class Auth or much better a scoped_model.
Here's a class implementation
class Auth {
get email() {
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString('email');
}
set email(String em) {
final SharedPreferences prefs = await SharedPreferences.getInstance();
pref.setString('email', em);
}
}
and now you can call it in your widgets :)
Try this;
make dart file (Filename and Class Name ShareUtils)
add follow Code
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:async';
class ShareUtils {
static ShareUtils _instance;
SharedPreferences ShareSave;
factory ShareUtils() => _instance ?? new ShareUtils._();
ShareUtils._();
void Instatce() async {
ShareSave = await SharedPreferences.getInstance();
}
Future<bool> set(key, value) async {
return ShareSave.setString(key, value);
}
Future<String> get(key) async {
return ShareSave.getString(key);
}
}
2.Add main.dart
class MyApp extends StatelessWidget {
static ShareUtils shareUtils;
#override
Widget build(BuildContext context) {
ThemeData mainTheme = new ThemeData(
);
shareUtils = new ShareUtils();
shareUtils.Instatce();
MaterialApp mainApp = new MaterialApp(
title: "Your app",
theme: mainTheme,
home: new SplashPage(),
debugShowCheckedModeBanner: true,
routes: <String, WidgetBuilder>{
"RegisterPage": (BuildContext context) => new RegisterPage(),
"HomePage": (BuildContext context) => new HomePage(),
},
);
return mainApp;
}
}
3.SET
void UserInfo(code, token) async{
await MyApp.shareUtils.set("token", token);
await MyApp.shareUtils.set("code", code);
await Navigator.of(context).pushNamed("HomePage");
}
4.GET
Future NextPage() async {
MyApp.shareUtils.get("token").then((token) {
print(token);
if (token == null || token == "") {
Navigator.of(context).popAndPushNamed("RegisterPage");
} else {
Navigator.of(context).popAndPushNamed("HomePage");
}
});
}
Hope to help.

Flutter - How to get value from shared preferences in a non-async method

I am trying to get some values saved in the SharedPreferences from a getter method of a class. But SharedPreferences.getInstance() returns a Future. Is there a way to obtain the SharedPreferences object in a non-async getter methods, for example:
import 'package:shared_preferences/shared_preferences.dart';
class MyClass {
get someValue {
return _sharedPreferencesObject.getString("someKey");
}
}
Is there something in Dart that is similar to .Result property in C#, for example getSomethingAsync().Result (https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1.result?view=netframework-4.7.2)?
You can use FutureBuilder()
SharedPreferences sharedPrefs;
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _getPrefs(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return YourFinalWidget();
}
return CircularProgressIndicator(); // or some other widget
},
);
}
Future<void> _getPrefs() async{
sharedPrefs = await SharedPreferences.getInstance();
}
You can do it in initState() and after this call setState() to update your build() method. Other way is to use FutureBuilder()
SharedPreferences sharedPrefs;
#override
void initState() {
super.initState();
SharedPreferences.getInstance().then((prefs) {
setState(() => sharedPrefs = prefs);
});
}
I suggest you use GetStorage
https://pub.dev/packages/get_storage
you will be able to get values without awaiting for them
main.dart
await GetStorage.init();
runApp(MyApp());
storage.dart
static bool isFirstOpen() {
final box = GetStorage();
return box.read("isFirstOpen") ?? true;
}
static setFirstOpen(bool isopen) {
final box = GetStorage();
box.write("isFirstOpen", isopen);
}
You can do something like this
Create a separate shared preferences service
Take in the SharedPreferences value in the constructor
You can then do get call without async, await
Example
class SharedPreferencesService {
final SharedPreferences _prefs;
const SharedPreferencesService(this._prefs);
Future<void> setString(String key, String value) async {
await _prefs.setString(key, value);
}
String? getString(String key) {
if (containsKey(key)) {
return _prefs.getString(key);
} else {
return null;
}
}
bool containsKey(String key) {
return _prefs.containsKey(key);
}
}
The only async call in SharedPreferences is to get the initial instance. This can be reused across your application. main can be async. So you can just await the SharedPreferences instance in main:
late SharedPreferences prefs;
main() async {
prefs = await SharedPreferences.getInstance();
runApp(App());
}
Now you can use prefs anywhere without resorting to async code. SharedPreferences will serve as a nonblocking write-through cache, with write operations running asynchronously in the background.
You can call a function on initState() and save SharedPreferences in a variable, so you can use in a non-async method, like this:
SharedPreferences prefs;
Future<void> loadPrefs() async {
prefs = await SharedPreferences.getInstance();
}
#override
void initState(){
super.initState();
loadPrefs();
}

Resources