Conver a stateLessWidget to function - flutter

I'm a new flutter developer.
I have a code to read data from firebase for one time
this code:
class GetUserName extends StatelessWidget {
final String documentId;
GetUserName(this.documentId);
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('users');
return FutureBuilder<DocumentSnapshot>(
future: users.doc(documentId).get(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {
return Text("Document does not exist");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data = snapshot.data!.data() as Map<String, dynamic>;
return Text("Full Name: ${data['full_name']} ${data['last_name']}");
}
return Text("loading");
},
);
}
}
it's work fine but I want to put these method into my Provider as function like this
Future<DocumentSnapshot> getUserName(String uid) => _database.doc(uid).snapshots();
so I want to put a function into provider class when I call this function it return a field data of this documents... (Replace GetUserName class as shown app, to be a function method only)
so how to write this function and how to call it as a map of data?
Edit:
as shown in this image:
here I got data as StreamBuilder and its work fine
here the explained method for stream in my provider class
as shown in the Following Image
Map<String, dynamic> data
I use data like
data['username']
it works fine so I want to put in My Provider class a function and returns a String, has two parameters for Example:
Text(myfunction(uid, value));
and it returns a string from (uid),
value = data[value]

add it before return HomePage();
auth.userData = data;
return HomePage();
add it in Auth provider
class Auth implements AuthBase {
Map<String,dynamic>? userData;
String getUserName(String parameter) {
return userData![parameter].toString();
}
}

Related

Can I Use a Future<String> to 'Fill In' a Text() Widget Instead of Using FutureBuilder in Flutter?

I'm trying to better understand Futures in Flutter. In this example, my app makes an API call to get some information of type Future<String>. I'd like to display this information in a Text() widget. However, because my String is wrapped in a Future I'm unable to put this information in my Text() widget, and I'm not sure how to handle this without resorting to a FutureBuilder to create the small widget tree.
The following example uses a FutureBuilder and it works fine. Note that I've commented out the following line near the bottom:
Future<String> category = getData();
Is it possible to turn category into a String and simply drop this in my Text() widget?
import 'package:flutter/material.dart';
import 'cocktails.dart';
class CocktailScreen extends StatefulWidget {
const CocktailScreen({super.key});
#override
State<CocktailScreen> createState() => _CocktailScreenState();
}
class _CocktailScreenState extends State<CocktailScreen> {
#override
Widget build(BuildContext context) {
Cocktails cocktails = Cocktails();
Future<String> getData() async {
var data = await cocktails.getCocktailByName('margarita');
String category = data['drinks'][0]['strCategory'];
print('Category: ${data["drinks"][0]["strCategory"]}');
return category;
}
FutureBuilder categoryText = FutureBuilder(
initialData: '',
future: getData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) {
return Text(snapshot.data);
} else if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
}
return const CircularProgressIndicator();
},
);
//Future<String> category = getData();
return Center(
child: categoryText,
);
}
}
Here's my Cocktails class:
import 'networking.dart';
const apiKey = '1';
const apiUrl = 'https://www.thecocktaildb.com/api/json/v1/1/search.php';
class Cocktails {
Future<dynamic> getCocktailByName(String cocktailName) async {
NetworkHelper networkHelper =
NetworkHelper('$apiUrl?s=$cocktailName&apikey=$apiKey');
dynamic cocktailData = await networkHelper.getData();
return cocktailData;
}
}
And here's my NetworkHelper class:
import 'package:http/http.dart' as http;
import 'dart:convert';
class NetworkHelper {
NetworkHelper(this.url);
final String url;
Future<dynamic> getData() async {
http.Response response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
String data = response.body;
var decodedData = jsonDecode(data);
return decodedData;
} else {
//print('Error: ${response.statusCode}');
throw 'Sorry, there\'s a problem with the request';
}
}
}
Yes, you can achieve getting Future value and update the state based on in without using Using FutureBuilder, by calling the Future in the initState(), and using the then keyword, to update the state when the Future returns a snapshot.
class StatefuleWidget extends StatefulWidget {
const StatefuleWidget({super.key});
#override
State<StatefuleWidget> createState() => _StatefuleWidgetState();
}
class _StatefuleWidgetState extends State<StatefuleWidget> {
String? text;
Future<String> getData() async {
var data = await cocktails.getCocktailByName('margarita');
String category = data['drinks'][0]['strCategory'];
print('Category: ${data["drinks"][0]["strCategory"]}');
return category;
}
#override
void initState() {
super.initState();
getData().then((value) {
setState(() {
text = value;
});
});
}
#override
Widget build(BuildContext context) {
return Text(text ?? 'Loading');
}
}
here I made the text variable nullable, then in the implementation of the Text() widget I set to it a loading text as default value to be shown until it Future is done0
The best way is using FutureBuilder:
FutureBuilder categoryText = FutureBuilder<String>(
future: getData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
var data = snapshot.data ?? '';
return Text(data);
}
}
},
),
but if you don't want to use FutureBuilder, first define a string variable like below and change your adasd to this :
String category = '';
Future<void> getData() async {
var data = await cocktails.getCocktailByName('margarita');
setState(() {
category = data['drinks'][0]['strCategory'];
});
}
then call it in initState :
#override
void initState() {
super.initState();
getData();
}
and use it like this:
#override
Widget build(BuildContext context) {
return Center(
child: Text(category),
);
}
remember define category and getData and cocktails out of build method not inside it.

How to set multiple StateNotifierProvider (s) with dynamicaly loaded async data?

I'm completely stuck with the task below.
So, the idea is to solve these steps using Riverpod
Fetch data from db with some kind of Future async while pausing the app (display SomeLoadingPage() etc.)
Once the data has loaded:
2.1 initialize multiple global StateNotifierProviders which utilize the data in their constructors and can further be used throughout the app with methods to update their states.
2.2 then show MainScreen() and the rest of UI
So far I've tried something like this:
class UserData extends StateNotifier<AsyncValue<Map>> { // just <Map> for now, for simplicity
UserData() : super(const AsyncValue.loading()) {
init();
}
Future<void> init() async {
state = const AsyncValue.loading();
try {
final HttpsCallableResult response =
await FirebaseFunctions.instance.httpsCallable('getUserData').call();
state = AsyncValue.data(response.data as Map<String, dynamic>);
} catch (e) {
state = AsyncValue.error(e);
}}}
final userDataProvider = StateNotifierProvider<UserData, AsyncValue<Map>>((ref) => UserData());
final loadingAppDataProvider = FutureProvider<bool>((ref) async {
final userData = await ref.watch(userDataProvider.future);
return userData.isNotEmpty;
});
class LoadingPage extends ConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
return FutureBuilder(
future: ref.watch(loadingAppDataProvider.future),
builder: (ctx, AsyncSnapshot snap) {
// everything here is simplified for the sake of a question
final Widget toReturn;
if (snap.connectionState == ConnectionState.waiting) {
toReturn = const SomeLoadingPage();
} else {
snap.error != null
? toReturn = Text(snap.error.toString())
: toReturn = const SafeArea(child: MainPage());
}
return toReturn;},);}}
I intentionally use FutureBuilder and not .when() because in future i may intend to use Future.wait([]) with multiple futures
This works so far, but the troubles come when I want to implement some kind of update() methods inside UserData and listen to its variables through the entire app. Something like
late Map userData = state.value ?? {};
late Map<String, dynamic> settings = userData['settings'] as Map<String, dynamic>;
void changeLang(String lang) {
print('change');
for (final key in settings.keys) {
if (key == 'lang') settings[key] = lang;
state = state.whenData((data) => {...data});
}
}
SomeLoadingPage() appears on each changeLang() method call.
In short:
I really want to have several StateNotifierProviders with the ability to modify their state from the inside and listen to it from outside. But fetch the initial state from database and make the intire app wait for this data to be fetched and these providers to be initilized.
So, I guess I figured how to solve this:
final futureExampleProvider = FutureProvider<Map>((ref) async {
final HttpsCallableResult response =
await FirebaseFunctions.instance.httpsCallable('getUserData').call();
return response.data as Map;
});
final exampleProvider = StateNotifierProvider<Example, Map>((ref) {
// we get AsyncValue from FutureNotifier
final data = ref.read(futureExampleProvider);
// and wait for it to load
return data.when(
// in fact we never get loading state because of FutureBuilder in UI
loading: () => Example({'loading': 'yes'}),
error: (e, st) => Example({'error': 'yes'}),
data: (data) => Example(data),
);
});
class LoadingPage extends ConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
return FutureBuilder(
// future: ref.watch(userDataProvider.future),
future: ref.watch(futureExampleProvider.future),
builder: (ctx, AsyncSnapshot snap) {
final Widget toReturn;
if (snap.data != null) {
snap.error != null
? toReturn = Text(snap.error.toString())
: toReturn = const SafeArea(child: MainPage());
} else {
// this is the only 'Loading' UI the user see before everything get loaded
toReturn = const Text('loading');
}
return toReturn;
},
);
}
}
class Example extends StateNotifier<Map> {
Example(this.initData) : super({}) {
// here comes initial data loaded from FutureProvider
state = initData;
}
// it can be used further to refer to the initial data, kinda like cache
Map initData;
// this way we can extract any parts of initData
late Map aaa = state['bbb'] as Map
// this method can be called from UI
void ccc() {
// modify and update data
aaa = {'someKey':'someValue'};
// trigger update
state = {...state};
}
}
This works for me, at least on this level of complexity.
I'll leave question unsolved in case there are some better suggestions.

Flutter The return type 'Map<int, int>' isn't a 'void', as required by the closure's context

Hi i want to return a map from a FirebaseDatabase. But i get the Error Code:
The return type 'Map<int, int>' isn't a 'void', as required by the closure's context.
if i print the map i get the right result. Im new in Flutter and i dont get it why its doesnt work. I guess i need to change the method type, but how?
String u1= 'Backsquat';
Dataread(String u1);
DatabaseReference data = FirebaseDatabase.instance.reference();
FirebaseAuth auth = FirebaseAuth.instance;
Map? read() {
Map <int,int> werte;
data.child("Kraftwerte").child(auth.currentUser.uid).child(u1).onValue.listen((event) {
werte = event.snapshot.value;
print(werte);
return werte;
}); ```
The error comes from the fact that your stream is asynchronous, while your actual function is synchronous.
If all you want is to return event.snapshot.value for every item on your stream, you can do this:
Stream<Map?> read() {
return data.child("Kraftwerte").child(auth.currentUser.uid).child(u1).onValue.map<Map>((event) => event.snapshot.value);
}
If what you want is to get the first value of the stream:
Future<Map?> read() async {
final event = await data.child("krafwerte").child(auth.currentUser.uid).child(u1).onValue.first;
return event.snapshot.value as Map?;
}
Either way, your code must be async
example of using the code:
class MyWidget extends StatelessWidget {
#override
Widget buuld(BuildContext context) {
return FutureBuilder(
future: _read(),
builder: (context, snapshot) {
if (snapshot.hasError) return Text(snapshot.error!);
if (snapshot.hasData) return Text(snapshot.data!['key']); // snapshot.data is the map, it's null if the future is not done.
return CircularProgressIndicator();
}
);
}
Future<Map> _read() {
final event = await data.child("krafwerte").child(auth.currentUser.uid).child(u1).onValue.first;
return event.snapshot.value as Map?;
}
}
To better understand how the future builder widget works, please read the future builder docs

Parameters from Stream<List> not being received in the MainPage

I'm trying to create a Stream, which will be called in the main page. This Stream returns me a list from my database. I will be using this list to create several cards in the main screen, and whenever there is a new card or a card removed, I will refresh the screen.
This is my Stream:
Stream<List> readData() async*{
Map<dynamic, dynamic> button_list = Map();
List lst = [];
final FirebaseUser user = await _auth.currentUser();
final lstValues = databaseReference.child(user.uid+"/buttons/").onValue.forEach((element) {
button_list = element.snapshot.value as Map;
lst = button_list.values.toList();
print(lst);
});
final lstStream = Stream.fromFuture(lstValues);
await for(var event in lstStream) {
yield lst;
}
}
This is the result from print(lst):
flutter: [{icon: delte, nome: Junior}, {icon: add, nome: Televisao}, {icon: bulb, nome: BAtata}]
This is the database:
This is the main screen with the main code:
body: StreamBuilder(
stream: _auth.readData(),
initialData: 0,
builder: (context, snapshot) {
if (snapshot.hasError || snapshot.hasError){
return Container(color: Colors.red);
}
if (!snapshot.hasData || !snapshot.hasData){
return Center(child: CircularProgressIndicator());
}
if (snapshot.hasData || snapshot.hasData){
return GridView.count(
The problem is that the values are not being received in the Stream. In the main page. Whenever I try to use snapshot.data I get nothing. At the moment the only think is loading is the progress circular indicator, I'm not receiving the content from the Stream I have created.
Personally, I rather work with streams and rxdart than methods such as yield.
Within my firebase projects I use a construction like this:
// Get a database reference for the user
Future<DatabaseReference> _getUserRef() async {
final FirebaseUser user = await _auth.currentUser();
return FirebaseDatabase.instance
.reference()
.child('users')
.child(user.uid);
}
// Get a reference to a specific user node. In you cause buttons
Future<DatabaseReference> _getButtonsRef() async {
return (await _getUserRef()).child('buttons');
}
// Get the data as stream
Stream<List<MyButton>> getButtons() { // Not sure what data type you need
return _getButtonsRef().asStream()
.switchMap((ref) => ref.onValue) // Use on value to get new data if any changes
.map((event) => event.snapshot.value != null ? // Map the value to the object you want or return an empty list
MySnapshotMapper.buttonListFromSnapshot(event.snapshot.value) : List<MyButton>()
);
}
In case you wonder about the MySnapshotMapper:
class MySnapshotMapper {
static List<MyButton> buttonListFromSnapshot(Map snapshot) {
return List<MyButton>.from(snapshot.values.map((snap) => MyButton.fromSnapshot(snap)));
}
}
And of course the button:
class MyButton {
// Not sure which fields it should have
String name = '';
double width = 10.0, height = 10;
MyButton.fromSnapshot(Map snap) {
name = snap['name'] ?? ''; // Use the value in the Map or or use a default value if not found
width = snap['width']?.toDouble() || width;
height = snap['height ']?.toDouble() || height ;
}
}
Step 1:
class EmployeeRepository {
final CollectionReference collection =
FirebaseFirestore.instance.collection('employees');
Stream<QuerySnapshot> getStream() {
/// Based on Firebase.auth you can collect user data here and pass as
/// Stream<QuerySnapshot> like below.
return collection.snapshots();
}
Future<List<Employee>> buildData(AsyncSnapshot snapshot) async {
List<Employee> list = [];
/// Based on the user snapShot, you can convert into the List and return to
/// the futurebuilder
await Future.forEach(snapshot.data.docs, (element) async {
list.add(Employee.fromSnapshot(element));
});
return Future<List<Employee>>.value(list);
}
}
Step 2:
EmployeeRepository employeeRepository = EmployeeRepository();
#override
Widget build(BuildContext context) {
Widget loadProgressIndicator() {
return Container(
child: Center(child: CircularProgressIndicator()),
);
}
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text('ListView'),
),
body: StreamBuilder<QuerySnapshot>(
stream: employeeRepository.getStream(),
builder: (context, snapShot) {
if (snapShot.hasError ||
snapShot.data == null ||
snapShot.data.docs.length == 0 ||
snapShot.connectionState == ConnectionState.waiting) {
return loadProgressIndicator();
} else {
return FutureBuilder(
future: employeeRepository.buildData(snapShot),
builder: (context, futureSnapShot) {
if (futureSnapShot.hasError ||
futureSnapShot.connectionState ==
ConnectionState.waiting ||
futureSnapShot.data.length == 0) {
return loadProgressIndicator();
} else {
return ListView.builder(
itemBuilder: (context, index) {
final employee = futureSnapShot.data[index];
return ListTile(
title: Text(employee.employeeName),
);
},
);
}
});
}
})));
}
This what I think has happened and which is why the code is not working as expected:
onValue function of the DocumentReference provides a Stream<Event> according to the latest documentation.
Stream<Event> onValue
But since the forEach returns a Future it is counted and used as a Future & then converted to a Stream by using Stream.fromFuture()
Future forEach(void action(T element))
While as forEach Returns a future, when completed it returns null as final value to the future.
Future forEach(void action(T element)) {
_Future future = new _Future();
StreamSubscription<T> subscription =
this.listen(null, onError: future._completeError, onDone: () {
future._complete(null);
}, cancelOnError: true);
subscription.onData((T element) {
_runUserCode<void>(() => action(element), (_) {},
_cancelAndErrorClosure(subscription, future));
});
return future;
}
Finally the lst being returned instead of the event in the final for loop.
await for (var event in lstStream) {
yield lst;
}
You can improve this code to make it work as following.
Stream<List> readData(user) async*{
final lstValues = databaseReference.child(user.uid+"/buttons/").onValue.map((element) {
Map button_list = element.snapshot.value as Map;
List lst = button_list.values.toList();
print(lst);
return lst;
}).toList();
final lstStream = Stream.fromFuture(lstValues);
await for(var event in lstStream) {
yield event;
}
}
Check that I have made following changes:
replaced forEach with map
[Optional change] taken Firebase user as method dependency as it is not required to be fetched on every iteration
[Optional change] moved lst & button_list inside the map execution block
I have not tested this code due to Firebase database dependency, but I have tested the theory on which this solution is based off of.
Here is the sample which I have tested:
Stream<List> readData() async* {
final list = Stream.fromIterable([
['a'],
['a', 'b'],
['a', 'b', 'c'],
['a', 'b', 'c', 'd']
]).map((element) {
print(element);
return element;
}).toList();
final listStream = Stream.fromFuture(list);
await for (var event in listStream) {
yield event;
}
}
I have replaced the Firebase document with a list of strings to make provide as much as resemblance as possible.
So in theory,
Stream.fromIterable([
['a'],
['a', 'b'],
['a', 'b', 'c'],
['a', 'b', 'c', 'd']
]) // Stream<List<String>> which can be similar to a list of documents
can replace
databaseReference.child(user.uid+"/buttons/").onValue // Stream<Event> which has a list of documents
Since FirebaseDatabase does not provide a stream of results you should use, Cloud FireStore
Here is the implementation of your code using cloud_firestore: ^0.16.0.
You will need to use subCollections for replicated the exact structure as RealTime Database.
1.Create a datamodel for the data you want to store and retrieve from firestore to made things easier.
class ButtonData{
final String name, icon;
ButtonData({this.name, this.icon});
}
Create a Stream that returns a list of documents from cloud firestore subCollection.
Stream<List<ButtonData>> getData(){
return users
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('buttons').snapshots().map(buttonsFromQuerySnapshot);
}
Create a function that converts QuerySnapshot from firestore to a list of required objects. buttonsFromQuerySnapshot
List<ButtonData> buttonsFromQuerySnapshot(QuerySnapshot querySnapshot){
return querySnapshot.docs.map((DocumentSnapshot snapshot) {
return ButtonData(name: snapshot.data()['name'].toString(), icon: snapshot.data()['icon'].toString());
}).toList();
}
Use a streamBuilder to show results from the stream.
StreamBuilder<List<ButtonData>>(
stream: getData(),
builder: (context, snapshot){
if (snapshot.hasData){
final List<ButtonData> buttons = snapshot.data;
return ListView.builder(itemBuilder: (context, index){
return Column(
children: [
Text(buttons[index].name),
Text(buttons[index].icon),
],
);
});
}
return const Center(child: CircularProgressIndicator(),);
}),
I would recommend you to store icons as integer values. Here you can
find a list of Material Icons and their integer values.
You can then display icons using their retrieved integer values. See
this answer https://stackoverflow.com/a/59854460/10285344 (Haven't
tried this)
I solved a very similar problem about loading the functions a user can execute according to their profile to build the interface. It's basically handling an async and futures issue. For me, Provider made the deal. I will try to put everything in order and paste my code for reference, note I did not have to make changes in the state, I just needed the initial information:
Create a multiprovider for your app
Define the Provider to call your API to get the initial information of the cards.
Pass this information as a parameter to your widget using Provider.of
Use this provider info in InitState()
Options for managing changes... Copy the provider info into an object you can handle or define API calls to your provider to update changes dynamically (I did not went through this)
Check relevant parts of code you may be interested in:
Provider class and API call:
class UserFunctionProvider {
Future<List<UserFunction>> loadUserFunctions() async {
return await APICall.profileFunctions();
}
}
static Future<List<UserFunction>> profileFunctions() async{
List<UserFunction> functionList = [];
UserFunction oneFunction;
final cfg = new GlobalConfiguration();
final token = window.localStorage["csrf"];
var res = await http.get('${cfg.get('server')}:${cfg.get('port')}/get_user_functions',
headers: {
'Content-type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token'
}
);
int i = 0;
jsonDecode(res.body).forEach((element) {
oneFunction = new UserFunction.fromJson(element);
oneFunction.tabControllerIndex = i;
i++;
functionList.add(oneFunction);
});
return functionList;
}
Defining a Multiprovider and passing it to the relevant widget (it was home in my case)
void main() async {
GlobalConfiguration().loadFromMap(AppConfiguration.appConfig);
Logger.root.level = Level.ALL; // defaults to Level.INFO
Logger.root.onRecord.listen((record) {
print(
'${record.level.name}: ${record.time}: ${record.loggerName}: ${record.message}');
});
WidgetsFlutterBinding.ensureInitialized();
FlutterError.onError = (FlutterErrorDetails details) {
FlutterError.dumpErrorToConsole(details);
if (kReleaseMode)
exit(1);
};
runApp(
MultiProvider(
providers: [
FutureProvider(create: (_) => UserFunctionProvider().loadUserFunctions()),
],
child: MyApp()
)
);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
String myLocale;
try{
myLocale = Platform.localeName;
}catch(e){
myLocale = 'es_ES';
print('Language set to Spanish by default.\n Error retrieving platform language: $e');
}
initializeDateFormatting(myLocale, null);
return MaterialApp(
title: 'Sanofi admin',
theme: ThemeData(primarySwatch: Colors.blue),
home: VerifySession().loadScreen(HomeScreen(Provider.of<List<UserFunction>>(context)))
);
}
}
Receiving the parameter from the provider into the Widget (as listOfUserFunction):
class HomeScreen extends StatefulWidget {
HomeScreen(this.listOfUserFunction);
final List<UserFunction> listOfUserFunction;
#override
HomeScreenState createState() => HomeScreenState();
}
class HomeScreenState extends State<HomeScreen>
with SingleTickerProviderStateMixin {
final log = Logger('HomeScreenState');
TabController tabController;
int active = 0;
UserFunction oneFunction;
#override
void initState() {
super.initState();
tabController = new TabController(vsync: this, length: widget.listOfUserFunction.length, initialIndex: 0)
..addListener(() {
setState(() {
active = tabController.index;
});
});
}

Flutter: Future<String> returns null but if printing it has value

username is null but if I'm printing 'value' it contains some string, how can I get 'value'?
class HomeWrapper extends StatelessWidget {
final DataBaseServices _db = DataBaseServices();
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
String username;
_db.getUsername(user).then((value) => username = value);
print(username);
if(username != null){
return Home();
}else{
_db.createBlankUser(user);
return EditProfile();
}
}
.then() is called when the value of the Future is returned. So the value of value is always non null, whereas username is null when you print it.
Try the difference by replacing .then(...) with:
.then((value){
username = value;
print(username);
});
Additionally, you can have a look at how to handle Asynchronous data in Flutter
I'm guessing _db.getUsername is returning a Future?
In that case you should look into using FutureBuilder
https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
return FutureBuilder(
builder: (context, snap) {
//snap.data will be the username
if(snap.hasData) {
return Home();
} else {
//you need to wait for another Future here I guess?
return FutureBuilder(
builder: (context, snap2){
if(snap2.connectionState == ConnectionState.done){
return EditProfile();
} else {
//return some sort of circular loader icon.
}
},
future: _db.createBlankUser(user)
);
}
},
future: _db.getUsername(user),
);