flutter: Not load data on first click - flutter

I want to show weather name on dialog box ,But when i Click first time on button data is not loading only show dialog box after that when i click second time then my data is loading.
I want to show weather name on dialog box ,But when i Click first time on button data is not loading only show dialog box after that when i click second time then my data is loading.
I want to show my data on first click please help how to do this.
This is my code
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:weather/weather.dart';
void main() => runApp(MyAppp());
class MyAppp extends StatefulWidget {
const MyAppp({ Key? key }) : super(key: key);
#override
_MyApppState createState() => _MyApppState();
}
class _MyApppState extends State<MyAppp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home:ButtonPage()
);
}
}
class ButtonPage extends StatefulWidget {
const ButtonPage({ Key? key }) : super(key: key);
#override
_ButtonPageState createState() => _ButtonPageState();
}
class _ButtonPageState extends State<ButtonPage> {
// passing this to latitude and longitude strings
String key = '856822fd8e22db5e1ba48c0e7d69844a';
late WeatherFactory ws;
List<Weather> _data = [];
double? lat, lon;
#override
void initState() {
super.initState();
ws = new WeatherFactory(key);
}
void getCurrentLocation() async {
var position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
var lat = position.latitude;
var long = position.longitude;
print(lat);
print(long);
queryWeather( lat,long) ;
}
queryWeather(var lat,var long) async {
print("Data: "+_data.toString());
/// Removes keyboard
FocusScope.of(context).requestFocus(FocusNode());
Weather weather = await ws.currentWeatherByLocation(lat, long);
setState(() {
_data = [weather];
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: RaisedButton(onPressed: () {
getCurrentLocation();
showDialog<void>(
context: context,
// barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return Dialog(
child: Center(
child: ListView.builder(
itemCount: _data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text("${_data[index].areaName}"),
);
},
),
),
);
},
);
},
child: Text("Button"),),),
);
}
}
I am using this permissions
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
weather: ^2.0.1
geolocator: ^7.4.0
url_launcher: ^6.0.9

Since getCurrentLocation is async, you have to mark your onPressed as async and await the result of getCurrentLocation:
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: RaisedButton(onPressed: () async {
await getCurrentLocation();
...
Also you have to await queryWeather call as well:
void getCurrentLocation() async {
var position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
var lat = position.latitude;
var long = position.longitude;
await queryWeather( lat,long) ;
}

Related

Flutter: auto complete widget from reading items from text file

I am trying to implement Autocomplete Widget and the items are present in a text file. While reading the file facing "A value of type 'Future<List?>' can't be assigned to a variable of type 'List?'." What am i missing?
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class ContractControl extends StatefulWidget {
const ContractControl({super.key});
#override
State<ContractControl> createState() => _ContractControlState();
}
class _ContractControlState extends State<ContractControl> {
//static const List<String> listItems = <String>['Apple', 'Banana'];
Future<List<String>?> getData() async {
try {
String fileData = await rootBundle.loadString('assets/instruments.txt');
List<String> lines = fileData.split('\n');
return lines;
} catch (e) {
throw (e.toString());
}
}
#override
Widget build(BuildContext context) {
List<String>? listItems = getData(); **--> here**
return Scaffold(
appBar: AppBar(title: const Text('Contract Control')),
body: Autocomplete<String>(
optionsBuilder: (TextEditingValue textEditingValue) {
Future<List<String>?> listItems = getData();
if (textEditingValue.text == '') {
return const Iterable<String>.empty();
}
return listItems.where((String item) {
return item.contains(textEditingValue.text.toUpperCase());
});
},
onSelected: (String item) {
print('Item selected');
},
),
);
}
}
getData() is async method. It is Future method, meaning it takes some time to execute. It should be like that: List<String>? listItems = await getData(); But it shows error because you are in build method which is not async. The best solution here is FutureBuilder widget!

Firebase Firestore and Google Maps Custom Marker only showing one marker

I'm trying to use the plugin Custom Marker to show on google maps, I got it close to working but it's only showing one marker from my model when I expect it to show all of them. Why is firestore only showing the one marker, how do I get the full list to show all of the markers instead of just one?
Edit 1: Removed old create marker function, that worked to make normal google map markers. Now all that's left is functioning code, it just only shows one marker instead of the entire list.
Edit 2: After reviewing the initial suggestion below I realize I'm getting a similar error message as to their suggestion.
Edit 3: Working code is at the bottom per suggestion by #Denzel
Unhandled Exception: setState() callback argument returned a Future.
E/flutter (31835): The setState() method on _HomePageState#73fe9 was called with a closure or method that returned a Future. Maybe it is marked as "async".
E/flutter (31835): Instead of performing asynchronous work inside a call to setState(), first execute the work (without updating the widget state), and then synchronously update the state inside a call to setState().
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:custom_marker/marker_icon.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:flutter/material.dart';
import '../models/marker_collect_model.dart';
class CustomMarkies extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<CustomMarkies> {
Set<Marker> list = <Marker>{};
List<String> listDocuments = [];
Future<void> readDataFromFirebase() async {
FirebaseFirestore firestore = FirebaseFirestore.instance;
CollectionReference<Map<String, dynamic>> collectionReference =
firestore.collection('2022TABR');
collectionReference.snapshots().listen((event) {
List<DocumentSnapshot> snapshots = event.docs;
for (var map in snapshots) {
Map<String, dynamic> data =
map.data() as Map<String, dynamic>; // add this line
MarkerCollectModel model =
MarkerCollectModel.fromMap(data); // use data here
String nameDocument = map.id;
listDocuments.add(nameDocument);
setState(() async {
Random random = Random();
int i = random.nextInt(10000);
String idString = 'id$i';
list.add(Marker(
markerId: MarkerId(idString),
icon: await MarkerIcon.downloadResizePicture(
url: model.urlavatar!, imageSize: 250),
position: LatLng(model.lat!, model.lng!),
);
});
}
});
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GoogleMap(
initialCameraPosition: CameraPosition(target: LatLng(45.4279, -123.6880), zoom: 3),
markers: list,
),
floatingActionButton: FloatingActionButton.extended(
label: FittedBox(child: Text('Add Markers')),
onPressed: () async {
readDataFromFirebase();
list.toSet();
setState(() {});
},
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
}
working code
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:custom_marker/marker_icon.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:flutter/material.dart';
import '../models/marker_collect_model.dart';
class CustomMarkies extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<CustomMarkies> {
Set<Marker> list = <Marker>{};
List<String> listDocuments = [];
Future<void> readDataFromFirebase() async {
FirebaseFirestore firestore = FirebaseFirestore.instance;
CollectionReference<Map<String, dynamic>> collectionReference =
firestore.collection('2022TABR');
collectionReference.snapshots().listen((event) async {
List<DocumentSnapshot> snapshots = event.docs;
for (var map in snapshots) {
Map<String, dynamic> data =
map.data() as Map<String, dynamic>; // add this line
MarkerCollectModel model =
MarkerCollectModel.fromMap(data); // use data here
String nameDocument = map.id;
listDocuments.add(nameDocument);
list.add(Marker(
markerId: MarkerId(nameDocument),
icon: await MarkerIcon.downloadResizePicture(
url: model.urlavatar!, imageSize: 250),
position: LatLng(model.lat!, model.lng!),
));
}
});
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GoogleMap(
initialCameraPosition: CameraPosition(target: LatLng(45.4279, -123.6880), zoom: 3),
markers: list,
),
floatingActionButton: FloatingActionButton.extended(
label: FittedBox(child: Text('Add Markers')),
onPressed: () async {
readDataFromFirebase();
list.toSet();
setState(() {});
},
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
}
class CustomMarkies extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<CustomMarkies> {
Set<Marker> list = <Marker>{};
List<String> listDocuments = [];
/// This [async] here covers for everything within the function,
/// You can use [await] anywhere you want to
/// [collectionReference.snapshots()] returns a [Stream], so you [listen] to it
/// There's no need for [await] because [Stream] handles real-time data
/// In essence, it would update all it's [listeners] about the new change and this change will take effect in your UI
Future<void> readDataFromFirebase() async {
FirebaseFirestore firestore = FirebaseFirestore.instance;
CollectionReference<Map<String, dynamic>> collectionReference =
firestore.collection('2022TABR');
collectionReference.snapshots().listen((event) {
for (var map in snapshots) {
Map<String, dynamic> data =
map.data() as Map<String, dynamic>; // add this line
MarkerCollectModel model =
MarkerCollectModel.fromMap(data); // use data here
String nameDocument = map.id;
listDocuments.add(nameDocument);
list.add(Marker(
markerId: MarkerId(nameDocument),
icon: await MarkerIcon.downloadResizePicture(
url: model.urlavatar!, imageSize: 250),
position: LatLng(model.lat!, model.lng!),
));
}
});
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GoogleMap(
initialCameraPosition:
CameraPosition(target: LatLng(45.4279, -123.6880), zoom: 3),
markers: list,
),
floatingActionButton: FloatingActionButton.extended(
label: FittedBox(child: Text('Add Markers')),
onPressed: () async {
// you have to `await readDataFromFirebase()` because it is a future.
await readDataFromFirebase();
list.toSet();
setState(() {});
},
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
}
I put the comments to explain further what I meant

Flutter async methods for widget initialize

Let's say I create a new screen team_screen which is the first parent of the tree.
Now for my team screen there are many widgets, some of theme have their own request, I want to show loader until every widget/request finished and ready.
I thought on 2 approaches.
All the requests are executed in team_screen with future builder and I pass the props to my widgets by demand.
Every widget with request get function that get executed in the async function in the initState function, then in my parent I make to every widget state parameter that is equal to true by the function I passed and when all is don't I stop the loader.
To sum up my problem is how to maintain a widget with many children and requests and showing one loader for entire page, making all the request on same widget? Pass isInitialize function to every widget?.
Which approach is better and if there are more approaches, I would like to hear.
Thank you for your help
Example for the second approach:
import 'package:flutter/material.dart';
import 'package:info_striker/locator.dart';
import 'package:info_striker/models/fixture/fixture.dart';
import 'package:info_striker/models/odds/bookmaker.dart';
import 'package:info_striker/models/synced-team/synced_team.dart';
import 'package:info_striker/services/fixture_service.dart';
import 'package:info_striker/utils/date_utilities.dart';
class TeamNextMatch extends StatefulWidget {
Function isInitialized;
SyncedTeam team;
TeamNextMatch({
Key? key,
required this.isInitialized,
required this.team,
}) : super(key: key);
#override
State<TeamNextMatch> createState() => _TeamNextMatchState();
}
class _TeamNextMatchState extends State<TeamNextMatch> {
Fixture? _fixture;
Bookmaker? _matchResult;
bool _isInitialized = false;
#override
void initState() {
super.initState();
init();
}
init() async {
final response = await locator<FixturesService>().getData(widget.team.id);
if (response != null) {
setState(() {
_fixture = Fixture.fromMap(response["fixture"]);
_matchResult = Bookmaker.fromMap(response["matchResultOdds"]);
});
}
widget.isInitialized(true);
}
#override
Widget build(BuildContext context) {
String? _date;
bool show = _fixture != null && _matchResult != null;
_fixture != null ? "${DateUtilities.getShortDateString(_fixture!.date)}, ${DateUtilities.getTimeString(_fixture!.date)}" : null;
return show
? Column(
children: [
Text(_fixture?.league?["name"]),
if (_date != null) Text(_date),
],
)
: const SizedBox();
}
}
You can show loader as described below -
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_application_1/data_model.dart';
import 'package:http/http.dart' as http;
class APiTest extends StatefulWidget {
const APiTest({Key? key}) : super(key: key);
#override
_APiTestState createState() => _APiTestState();
}
class _APiTestState extends State<APiTest> {
final String _url = "https://jsonplaceholder.typicode.com/todos/";
bool _isLoading = true;
final List<DataModel> _allData = [];
#override
void initState() {
super.initState();
_initData().then((value) {
setState(() {
_isLoading = false;
});
});
}
Future<void> _initData() async {
final response = await http.get(Uri.parse(_url));
final List res = jsonDecode(response.body);
res.forEach((element) {
_allData.add(DataModel.fromJson(element));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Loading Demo"),
),
body: Stack(
children: [
ListView.separated(
itemCount: _allData.length,
controller: ScrollController(),
separatorBuilder: (_, __) => const SizedBox(height: 10),
itemBuilder: ((context, index) {
return ListTile(
tileColor: Colors.grey[200],
title: Text(_allData[index].title!),
subtitle: Text(_allData[index].id.toString()),
);
}),
),
if (_isLoading)
const Center(
child: CircularProgressIndicator(),
)
],
),
);
}
}

Is it possible to have separate BuildContext for two dialogs in Flutter?

I want to control how I close specific dialogs in Flutter. I know if I call Navigator.of(context).pop() it will close the latest dialog.
However my situation is that I can have two dialogs opened at the same time in different order (a -> b or b -> a) and I want to explicitly close one of them.
I know that showDialog builder method provides a BuildContext that I can reference and do Navigator.of(storedDialogContext).pop() but that actually doesn't really help since this context shares same navigation stack.
Update: Vandan has provided useful answer. One solution is to use Overlay widget but it has its downsides, see this answer
My example is on dartpad.dev, example code:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Completer<BuildContext>? _dialog1Completer;
Completer<BuildContext>? _dialog2Completer;
bool _opened1 = false;
bool _opened2 = false;
#override
void initState() {
super.initState();
Timer(const Duration(seconds: 3), () {
_openDialog1();
debugPrint('Opened dialog 1. Dialog should read: "Dialog 1"');
Timer(const Duration(seconds: 2), () {
_openDialog2();
debugPrint('Opened dialog 2. Dialog should read: "Dialog 2"');
Timer(const Duration(seconds: 3), () {
_closeDialog1();
debugPrint('Closed dialog 1. Dialog should read: "Dialog 2"');
Timer(const Duration(seconds: 5), () {
_closeDialog2();
debugPrint('Closed dialog 2. You should not see any dialog at all.');
});
});
});
});
}
Future<void> _openDialog1() async {
setState(() {
_opened1 = true;
});
_dialog1Completer = Completer<BuildContext>();
await showDialog(
barrierDismissible: false,
context: context,
routeSettings: const RouteSettings(name: 'dialog1'),
builder: (dialogContext) {
if (_dialog1Completer?.isCompleted == false) {
_dialog1Completer?.complete(dialogContext);
}
return CustomDialog(title: 'Dialog 1', timeout: false, onClose: _closeDialog1);
});
}
Future<void> _openDialog2() async {
setState(() {
_opened2 = true;
});
_dialog2Completer = Completer<BuildContext>();
await showDialog(
barrierDismissible: false,
context: context,
routeSettings: const RouteSettings(name: 'dialog1'),
builder: (dialogContext) {
if (_dialog2Completer?.isCompleted == false) {
_dialog2Completer?.complete(dialogContext);
}
return CustomDialog(title: 'Dialog 2', timeout: false, onClose: _closeDialog2);
});
}
Future<void> _closeDialog1() async {
final ctx = await _dialog1Completer?.future;
if (ctx == null) {
debugPrint('Could not closed dialog 1, no context.');
return;
}
Navigator.of(ctx, rootNavigator: true).pop();
setState(() {
_dialog1Completer = null;
_opened1 = false;
});
}
Future<void> _closeDialog2() async {
final ctx = await _dialog2Completer?.future;
if (ctx == null) {
debugPrint('Could not closed dialog 2, no context.');
return;
}
Navigator.of(ctx, rootNavigator: true).pop();
setState(() {
_dialog2Completer = null;
_opened2 = false;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
TextButton(onPressed: _openDialog1, child: const Text('Open 1')),
TextButton(onPressed: _openDialog2, child: const Text('Open 2')),
const Spacer(),
Align(
alignment: Alignment.bottomCenter,
child: Text('Opened 1? $_opened1\nOpened 2? $_opened2'),
),
],
),
),
);
}
}
class CustomDialog extends StatefulWidget {
const CustomDialog({
Key? key,
required this.timeout,
required this.title,
required this.onClose,
}) : super(key: key);
final bool timeout;
final String title;
final void Function() onClose;
#override
createState() => _CustomDialogState();
}
class _CustomDialogState extends State<CustomDialog>
with SingleTickerProviderStateMixin {
late final Ticker _ticker;
Duration? _elapsed;
final Duration _closeIn = const Duration(seconds: 5);
late final Timer? _timer;
#override
void initState() {
super.initState();
_timer = widget.timeout ? Timer(_closeIn, widget.onClose) : null;
_ticker = createTicker((elapsed) {
setState(() {
_elapsed = elapsed;
});
});
_ticker.start();
}
#override
void dispose() {
_ticker.dispose();
_timer?.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(widget.title),
content: SizedBox(
height: MediaQuery.of(context).size.height / 3,
child: Center(
child: Text([
'${_elapsed?.inMilliseconds ?? 0.0}',
if (widget.timeout) ' / ${_closeIn.inMilliseconds}',
].join('')))),
actions: [
TextButton(onPressed: widget.onClose, child: const Text('Close'))
],
);
}
}
If you were to run this code and observe console you can see steps being printed, on step #3 you can observe unwanted behaviour:
opened dialog 1 - OK
opened dialog 2 - OK
closed dialog 1 - not OK
I think I understand the problem, Navigator.of(dialogContext, rootNavigator: true) searches for nearest navigator and then calls .pop() method on it, removing the latest route/dialog on its stack.
I would need to remove specific dialog.
What would be the solution here? Multiple Navigator objects?
I highly suggest that in this case you use Overlay in Flutter. Overlays are rendered independently of widgets on the screen and have their own lifetimes. They appear when you ask them to and you can control when and which one of them should disappear at which time.

Flutter - Drawer as sub-class not updating

I'm a fairly inexperienced coder.
I have a Drawer which I have created as a separate class. The issue I'm having is the dynamic data for the Drawer is not populating.
I am expecting the data being retrieved from Shared Preferences should populate the third line of my view with the value of currUserL.
It's being evaluated correctly, and returns the value of currUserL to the console, but is not updated in the Drawer.
I've loaded up a about button (triggering the update method) that works when pressed manually, but data persists only while the drawer remains open. It reverts when the drawer is closed.
drawerPatient.dart
class DrawerPatient extends StatefulWidget {
DrawerPatient({Key key}) : super(key: key);
#override
_DrawerPatientState createState() => new _DrawerPatientState();
}
class _DrawerPatientState extends State<DrawerPatient> {
String currUserL = "nv3";
Future getPref() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
currUserL = prefs.getString('currUserLast');
debugPrint('user: $currUserL');
}
#override
void initState() {
getPref();
}
void update() {
setState(() {
getPref();
});
}
#override
Widget build(BuildContext context) {
return Drawer(
child: new ListView(
children: <Widget>[
new DrawerHeader(
child: new Text('Patient Management'),
),
new ListTile(
title: new Text('search'),
onTap: () {},
),
new ListTile(
title: new Text(currUserL),
onTap: () {},
),
new Divider(),
new ListTile(
title: new Text('About'),
onTap: update,
),
],
));
}
}
userList.dart
class UserList extends StatefulWidget {
UserList({Key key, this.title}) : super(key: key);
final String title;
final String titleHead = "User List";
#override
_UserListState createState() => new _UserListState();
}
class _UserListState extends State<UserList> {
: sortStr}, headers: {"Accept": "application/json"});
setState(() {
data = json.decode(response.body);
});
}
#override
void initState() {
this.makeRequest();
// DrawerPatient().createState().update();
}
void _refresh() {
setState(() {});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Patient List"),
drawer: new DrawerPatient(key: new UniqueKey()),
...
Drawer when opened
Drawer after clicking about (update)
So I found the answer, thanks to #Dinesh for pointing me in the right direction.
The answer was to put the setState as a dependency on the async get prefs.
Future getPref() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
currUserI = prefs.getString('currUserId');
currUserF = prefs.getString('currUserFirst');
currUserL = prefs.getString('currUserLast');
debugPrint('user: $currUserL');
});
}
Can you try this,
Future getCurrentUser() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString('currUserLast');
}
void update() {
val tempName = getCurrentUser();
setState(() {
currUserL = tempName;
});
}
Reason: Basically wait for the async method before calling setState