LateInitializationError: Field 'searchResults' has not been initialized - flutter

I tried to used google map autocomplete, but I got the this error LateInitializationError: Field 'searchResults' has not been initialized.
How can solve it problem?
I still tried to used this way, but still falue.
void initState() {
searchResults;
}
This is my code
late List<PlaceSearch> searchResults;
final placeService = PlaceSerive();
searchPlaces(String searchTerm) async {
searchResults = await placeService.getAutoComplete(searchTerm);
}
...
...
void initState() {
searchResults;
}
...
...
...
child: ListView.builder(
itemCount: searchResults.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(searchResults[index].description,
style: TextStyle(color: Colors.black)),
);
},
),

While searchResults will get data from future it is better to use FutureBuilder for statelesswidget or nullabe data.
On state class
List<PlaceSearch>? searchResults;
#override
void initState() {
super.initState();
searchPlaces("yourSearchTerm")
}
searchPlaces(String searchTerm) async {
searchResults = await placeService.getAutoComplete(searchTerm);
setState((){});
}
Now you can check whether list is null or not.
child: searchResults==null? Text("loading") :ListView.builder(...)

You are not calling the searchPlaces function in the init function so it can't initialize the searchResults variable. To solve it do this:
#override
initState(){
super.initState();
searchPlaces();
}
and remember to set state in searchPlaces function.

Related

How to run async await code which is from store of provider inside initState

I'm trying to get data from API when my app starts. Therefore, I wrote the async await function at a store of Provider to bring API data. However, it doesn't work and sends 404 status code. How can I fix this problem?
main.dart codes
#override
void initState(){
super.initState();
// this is the code from provider
context.read<HomeData>().getData();
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 4,
child: Scaffold(
appBar: TopAppBar(
),
body: BodyPage(
changePage : _changePage,
pageController : _pageController,
currentIndex : _currentIndex,
),
bottomNavigationBar: BottomBar(currentIndex: _currentIndex, pageController: _pageController),
extendBodyBehindAppBar: true,
),
);
}
Provider Store codes
class HomeData extends ChangeNotifier {
var homeData = [];
var boardItem = {};
late String _image;
String get image => _image;
late String _content;
String get content => _content;
var _now;
DateTime get now => _now;
// 처음 홈에서 렌더링 될 때 나오는 아이템 데이터를 불러옴
getData() async{
var rawData = await http.get(Uri.parse('https://codingapple1.github.io/app/Constants.json'));
if (rawData.statusCode == 200) {
homeData = jsonDecode(rawData.body);
print(homeData);
}
else{
print('statusCode : ${rawData.statusCode}');
}
notifyListeners();
}
}
As the issue is 404 means page is not found(available). Try rechecking the link path.
You will find context issue until first frame gets build. You can use addPostFrameCallback.
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await context.read<HomeData>().getData();
});
}
Also you can try using FutureBuilder.
You can use FutureBuilder or WidgetsBinding or you can put it in your init state; Future.delayed(Duration.zero, ()async{await xx();},);

Flutter - Async function not being waited for

appreciate the help! I've looked through some of the other responses on here and I can't find an answer.
I have a Provider, in which I have an async function defined. It reaches out to an external API, gets data, and then is meant to update the attributes in the Provider with the data received.
The Widget that uses the provider is meant to build a ListView with that data. projects is null until the response is received. That's why I need the async await functionality to work here. The error I'm getting says that "length can't be called on null", which means projects is still null at the time is reaches that line. That is because the async functionality isn't working.
Here is the Provider, in which my async function is defined:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import '../../constants/urls.dart';
import 'project.dart';
class Projects with ChangeNotifier{
List<Project> _projects;
List<Project> _myProjects;
final String authToken;
final List<Project> previousProjects;
final bool _initialLoad = true;
Projects(this.authToken, this.previousProjects);
List<Project> get projects {
return _projects;
}
List<Project> get myProjects {
return _myProjects;
}
bool get initialLoad {
return _initialLoad;
}
Future<void> fetchProjects() async {
print('inside future, a');
try {
var response = await http.get(
Uri.parse(Constants.fetchProjectsURL),
headers: {"Authorization": "Bearer " + authToken},
);
print('inside future, b');
if (response.statusCode == 200) {
final extractedData = json.decode(response.body) as List;
final List<Project> tempLoadedProjects = [];
extractedData.forEach((project) {
tempLoadedProjects.add(
Project(
// insert project params
),
);
});
_projects = tempLoadedProjects;
print(_projects);
print(projects);
notifyListeners();
} else {
print('something happened');
}
} catch (error) {
throw error;
}
}
}
Then, I used this provider in the following Widget:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../../providers/projects/projects_provider.dart';
class ProjectsColumn extends StatelessWidget {
Future<void> fetchProjects(ctx) async {
await Provider.of<Projects>(ctx).fetchProjects();
}
Widget build(BuildContext context) {
print('Before fetch');
fetchProjects(context);
print('After fetch');
final projects = Provider.of<Projects>(context, listen: false).projects;
return ListView.builder(
itemCount: projects.length,
itemBuilder: (BuildContext ctx, int index) {
return Card(
child: Text(
'Project Name:${projects[index]}',
),
);
});
}
}
Thoughts?
You need to put await before the method to a wait, but you can't do this in build() method, So you can use future builder like the answer of #jamesdlin
or you can call fetchProjects method in intState first like this way:
class ProjectsColumn extends StatefulWidget {
#override
State<ProjectsColumn> createState() => _ProjectsColumnState();
}
class _ProjectsColumnState extends State<ProjectsColumn> {
bool _isLoading = true;
Future<void> _fetchProjects() async {
await Provider.of<Projects>(context, listen: false).fetchProjects();
_isLoading = false;
if (mounted) setState(() {});
}
#override
void initState() {
super.initState();
_fetchProjects();
}
#override
Widget build(BuildContext context) {
return _isLoading
? const Center(child: CircularProgressIndicator())
: Consumer<Projects>(
builder: (context, builder, child) => builder.projects.isEmpty
? const Center(child: Text('No Projects Found'))
: ListView.builder(
shrinkWrap: true,
itemCount: builder.projects.length,
itemBuilder: (BuildContext ctx, int index) {
return Card(
child: Text(
'Project Name:${builder.projects[index]}',
),
);
},
),
);
}
}
EDIT:
a) From the docs HERE BuildContext objects are passed to WidgetBuilder functions (such as StatelessWidget.build), and are available from the State.context member., and in the previous example I used StatefulWidget widget that extends state class, then you can use context outside build but inside the class extends state, not like StatelessWidget.
b) mounted condition, it represents whether a state is currently in the widget tree, i used it to prevent the famous error: setState() called after dispose()
see docs HERE, also this useful answer HERE

flutter - FutureBuilder auto rebuild each time press a button in the screen

I try to use FutureBuilder in Flutter to wait ulti my initState is finished then buil the UI for the app.
But when the app is running, the screen keep rebuilding each time I press another button (the button does totally different thing).
Future loadUser() async {
String jsonString = await storage.read(key: "jwt");
final jsonResponse = json.decode(jsonString);
loggedUser = new LoggedUser.fromJson(jsonResponse);
print(loggedUser.token);
getProfile();
getJourneyByUserId()
.then((receivedList){
addRanges(receivedList);});
}
Future<List<Journey>>getJourneyByUserId() async {
var res = await http.get(
Uri.parse("$baseUrl/journeys/userid=${loggedUser.user.userId}"),
headers: {
'Content_Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer ${loggedUser.token}',
},
);
if (res.statusCode == 200) {
print("Get journeys successfully");
}
var data = jsonDecode(res.body);
List idList = [];
for (var i in data) {
idList.add(i["journeyId"]);
}
for (var i in idList) {
var res = await http.get(
Uri.parse("$baseUrl/journeys/$i"),
);
var data = jsonDecode(res.body);
Journey userJourney = new Journey.fromJson(data);
setState(() {
journeyList.add(userJourney);
});
}
print("Journey ${journeyList.length}");
return journeyList;
}
addRanges(journeyList){
setState(() {
rangeList=[];
});
if (journeyList.isNotEmpty) {
for (var i in journeyList) {
DateTime startDate =
DateTime(i.startDate.year, i.startDate.month, i.startDate.day);
DateTime endDate =
DateTime(i.endDate.year, i.endDate.month, i.endDate.day);
setState(() {
rangeList.add(PickerDateRange(startDate, endDate));
});
}
}
print("Range ${rangeList.length}");
return rangeList;
}
returnRange() {
List<PickerDateRange> list = [];
for(int i =0; i<rangeList.length;i++){
list.add(rangeList[i]);
}
return list;
}
Future functionForBuilder() async {
return await returnRange();
}
//initState function
#override
void initState() {
super.initState();
loadUser();
functionForBuilder();
}
//build the UI
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("$_name's Profile",style: TextStyle(color: kColorPalette4),),
centerTitle: true,
),
body: Container(
child: FutureBuilder(
future: functionForBuilder(),
builder: (BuildContext context,AsyncSnapshot snapshot){
//here I set the condition for each case of snapshot
}
I have read some documents say that I should assign the functionForBuilder() to a Future variable when initState then use it in the future child of FutureBuilder. Example:
Future _future;
//initState function
#override
void initState() {
super.initState();
loadUser();
_future=functionForBuilder();
}
// then with the FutureBuilder
future: _future
With this way the screen is not rebuild anymore but my function returnRange() seems like not running as my expextation (I called the returnRange() once in the build() function).
Thanks in advance for your answer!
Whenever you assign to the _future variable again, you must do that inside a setState block, otherwise the widget will not rebuild with the new future.
For example:
void updateData() {
setState(() {
_future = functionForBuilder();
});
}
If you use FutureBuilder, it rebuild items again and again.
Try two ways:
Don't use `future: functionForBuilder(), comment it.
Remove FutureBuilder(), simply use Container().
And let me know any issue?
Code:
call your future in the initstate method not in the build as shown in the example.
class MyPage extends StatefulWidget { #override State<MyPage> createState() => _MyPageState(); } class _MyPageState extends State<MyPage> { // Declare a variable. late final Future<int> _future; #override void initState() { super.initState(); _future = _calculate(); // Assign your Future to it. } // This is your actual Future. Future<int> _calculate() => Future.delayed(Duration(seconds: 3), () => 42); #override Widget build(BuildContext context) { return Scaffold( body: FutureBuilder<int>( future: _future, // Use your variable here (not the actual Future) builder: (_, snapshot) { if (snapshot.hasData) return Text('Value = ${snapshot.data!}'); return Text('Loading...'); }, ), ); } }

Unable to fetch the selected value of Fortune Wheel

I am using the Fortune Wheel in this link. When I fling the wheel, it will spin and end but when it is done, I'm unable to fetch the value of the selection.
Here's what I have tried:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_fortune_wheel/flutter_fortune_wheel.dart';
class SpinawheelWidget extends StatefulWidget {
#override
State<StatefulWidget> createState() => SpinawheelState();
}
class SpinawheelState extends State<SpinawheelWidget> {
StreamController<int> selected = StreamController<int>();
#override
void initState() {
super.initState();
}
#override
void dispose() {
selected.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
final items = <String>[
'item1',
'item2',
'item3',
'item4',
'item5',
'item6',
];
return Column(
children: [
Expanded(
child: FortuneWheel(
physics: CircularPanPhysics(
duration: Duration(seconds: 1),
curve: Curves.decelerate,
),
onFling: () {
print('onFling');
selected.add(1);
},
onAnimationStart: () {
print('animation start');
},
onAnimationEnd: () {
print('animation end ${selected.stream}');
},
animateFirst: false,
selected: selected.stream,
items: [
for (var it in items) FortuneItem(child: Text(it)),
],
),
),
],
);
}
}
The print on onAnimationEnd only shows: animation end Instance of '_ControllerStream' but not the value. I am expect to get at least one of the item or the position of the item. Please help. Thanks!
Got it to work. Just need some changes.
Change your declaration of stream controller to broadcast
StreamController<int> _controller = StreamController.broadcast();
Then the trick. In some place of your widget tree (mine was just above the FortuneWheel in a Column) insert this StreamBuilder. This will show nothing, just handle some functions. In order to let us handle the snapshot result, we wil add the _text funciontion returning a widget. See bellow:
StreamBuilder(
stream: _controller.stream,
builder: (context, snapshot) => snapshot.hasData
? _text(snapshot)
: Container(),
)
And now, the function returning the widget _text.
Widget _text(var snapshot) {
//here you cand get and handle the result and do whathever.
print(snapshot.data);
int val = snapshot.data;
print(items[val]);
return Text(snapshot.data.toString()); //you dont need to return //anything here. Just replace the Text widget with SizedBox() to return //nothing.
}
As, the library is using Stream you will need to use stream method to get data from selection.One such method is called listen()
Rest you can check this : Medium Link - Streams In Flutter
var rendomval = Fortune.randomInt(0, items.length); setState(() { selected.add(rendomval); }); print(rendomval);
Declare the stream controller like this
StreamController<int> _controller = StreamController.broadcast();
then call the listen whatever you want , suggesting use listener inside initState function
_controller.stream.listen((value) {
print('Value from controller: $value');
})

How to reference variable in method in FutureBuilder (builder:)?

I want to use the variable dbRef in inputData() in future Builder builder: you can see the variable in between asterisk .
void inputData() async {
FirebaseUser user = await FirebaseAuth.instance.currentUser();
final uid = user.uid;
final **dbRef** = FirebaseDatabase.instance.reference().child("Add Job Details").child(uid).child("Favorites");
}
#override
Widget build(BuildContext context) {
return FutureBuilder (
future: **dbRef**.once(),
builder: (context, AsyncSnapshot<DataSnapshot> snapshot) {
if (snapshot.hasData) {
List<Map<dynamic, dynamic>> list = [];
for (String key in snapshot.data.value.keys) {
list.add(snapshot.data.value[key]);
}
This is one more approach to tackle the problem.
The idea is to use a variable _loading and set it to true initially.
Now, after in your inputData() function, you can set it to false once you get the dbref.
Store dbref, the way I stored _myFuture in the code below i.e., globally within the class.
Use your _loading variable to return a progress bar if its true else return FutureBuilder with your dbref.once() in place. Now, that you have loaded it, it should be available at this point.
class MyWidget extends StatefulWidget {
#override
createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
// Is the future being loaded?
bool _loading;
// This is the future we will be using in our FutureBuilder.
// It is currently null and we will assign it in _loadMyFuture function.
// Until assigned, we will keep the _loading variable as true.
Future<String> _myFuture;
// Load the _myFuture with the future we are going to use in FutureBuilder
Future<void> _loadMyFuture() async {
// Fake the wait for 2 seconds
await Future.delayed(const Duration(seconds: 2));
// Our fake future that will take 2 seconds to return "Hello"
_myFuture = Future(() async {
await Future.delayed(const Duration(seconds: 2));
return "Hello";
});
}
// We initialize stuff here. Remember, initState is called once in the beginning so hot-reload wont make flutter call it again
#override
initState() {
super.initState();
_loading = true; // Start loading
_loadMyFuture().then((x) => setState(() => _loading = false)); // Set loading = false when the future is loaded
}
#override
Widget build(BuildContext context) {
// If loading, show loading bar
return _loading?_loader():FutureBuilder<String>(
future: _myFuture,
builder: (context, snapshot) {
if(!snapshot.hasData) return _loader(); // still loading but now it's due to the delay in _myFuture
else return Text(snapshot.data);
},
);
}
// A simple loading widget
Widget _loader() {
return Container(
child: CircularProgressIndicator(),
width: 30,
height: 30
);
}
}
Here is the output of this approach
This does the job but, you might need to do it for every class where you require your uid.
========================================
Here is the approach I described in the comments.
// Create a User Manager like this
class UserManager {
static String _uid;
static String get uid => _uid;
static Future<void> loadUID() async {
// Your loading code
await Future.delayed(const Duration(seconds: 5));
_uid = '1234'; // Let's assign it directly for the sake of this example
}
}
In your welcome screen:
class MyWidget extends StatefulWidget {
#override
createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
bool _loading = true;
#override
void initState() {
super.initState();
UserManager.loadUID().then((x) => setState(() => _loading = false));
}
#override
Widget build(BuildContext context) {
return _loading ? _loader() : Text('Welcome User ${UserManager.uid}!');
}
// A simple loading widget
Widget _loader() {
return Container(child: CircularProgressIndicator(), width: 30, height: 30);
}
}
The advantage of this method is that once you have loaded the uid, You can directly access it like this:
String uid = UserManager.uid;
thus eliminating use of futures.
Hope this helps!