Cant Update Array With Dropdown Button And Future Flutter - flutter

I am using a dropdown button to update locations of restaurants around a user but, the locations aren't updating in the ListView. Here is what I have so far:
Edit: Added Init statement
Init Statement
#override
void initState() {
_dropDownMenuItems = buildAndGetDropDownMenuItems(changeDistance);
_mapDropdownFilter = _dropDownMenuItems[0].value;
filterMarker(userLat, userLong, dispensaries, 1);
super.initState();
}
I set up a class to assist with this called locations
class Locations{
final Restaurant dis;
final double dist;
final int index;
Locations({this.dis, this.dist, this.index});
}
Then call this filter to sort my location by distance and give them a distance in miles from user:
Future filterMarker(userLat, userLong, restaurants, filterdis) async {
Set<Marker> _tmpMarkers = Set();
final Uint8List markerIcon =
await getBytesFromAsset('assets/smiley.png', 100);
int filterCounter = 0;
List<Locations> _tmpLocation = List();
for (int i = 0; i < restaurant.length; ++i) {
Geolocator()
.distanceBetween(
userLat, userLong, restaurants[i].lat, restaurants[i].long)
.then((calcDist) {
print(calcDist);
if (calcDist / 1000 < filterdis) {
filterCounter++;
final String markerIdVal = 'marker_id_$filterCounter';
final MarkerId markerId = MarkerId(markerIdVal);
var dis = calcDist / 1000;
var distances = dis.toStringAsFixed(2);
// creating a new MARKER
_tmpMarkers.add(Marker(
markerId: markerId,
position: LatLng(restaurants[i].lat, restaurants[i].long),
infoWindow: InfoWindow(
title: restaurants[i].name, snippet: '$distances mi Away'),
icon: BitmapDescriptor.fromBytes(markerIcon)));
_tmpLocation.add(
Locations(index: i, dis: restaurants[i], dist: calcDist / 1000));
}
});
setState(() {
_filteredMarkers = _tmpMarkers;
filteredVenues = _tmpLocation.cast<Locations>();
});
print('Do once');
}
print('There are ${filteredVenues.length} _filteredMarkers');
}
My drop down button button is setup like this:
DropdownButton(
style: TextStyle(
fontFamily: 'Roboto-Regular',
color: Colors.black),
elevation: 6,
icon: Icon(
FontAwesomeIcons.angleRight,
color: buddiesGreen,
),
value: _mapDropdownFilter,
items: _dropDownMenuItems,
onChanged: _changeFilterList,
)
Filled with this:
//Distance Map filter Button
final List changeDistance = ['1', '5 ', '10 ', '20'];
// Set<Marker> _filteredMarkers = Set();
List<Locations> filteredVenues;
var _filteredMarkers;
var filteredRestaurant;
_dropDownMenuItems = buildAndGetDropDownMenuItems(changeDistance);
_mapDropdownFilter = _dropDownMenuItems[0].value;
String _mapDropdownFilter;
List<DropdownMenuItem<String>> _dropDownMenuItems;
List<DropdownMenuItem<String>> buildAndGetDropDownMenuItems(List tempList) {
List<DropdownMenuItem<String>> items = List();
for (String x in tempList) {
items.add(DropdownMenuItem(
value: x,
child: Text(
x,
style: TextStyle(
fontSize: 14,
fontFamily: 'Roboto-Regular',
fontWeight: FontWeight.w700,
color: buddiesPurple),
)));
}
return items;
}
void _changeFilterList(String value) {
//use _filterDropdown for switch statement
setState(() {
_mapDropdownFilter = value;
});
_sortProductsDropDown(_mapDropdownFilter);
print(_mapDropdownFilter);
}
void _sortProductsDropDown(_mapDropdownFilter) {
switch (_mapDropdownFilter) {
case '1 Mile':
print('1 Mile');
filterMarker(userLat, userLong, restaurant, 1);
print(restaurant.length);
break;
case '5 Miles':
filterMarker(userLat, userLong, restaurant, 5);
print(restaurant.length);
break;
case '10 Miles':
filterMarker(userLat, userLong, restaurant, 10);
print(restaurant.length);
break;
case '20 Miles':
filterMarker(userLat, userLong, restaurant, 20);
print(restaurant.length);
break;
}
}
It doesn't update when I hit the dropdown where is the disconnect and why doesn't it up date when the user selects different distances.

because you're not using setState() in initState() where you're fetching the list of locations. So the state of Locations isn't updated even though you fetch everything correctly.
..
setState(){
_dropDownMenuItems = buildAndGetDropDownMenuItems(changeDistance);
..
}
do something like this.

Related

How to delete or update a Polygon by touching a marker?

I wrote these 3 functions to draw a polygon. It works perfectly except the editing and deleting part. Please let me know where I have done mistakes. I tried to update all of the markers and coordinates lists/sets. It works, but I got RangeError (index): Invalid value: Only valid value is 0: 2.
void _onMapLongPress(LatLng latLng) {
// final markerId = MarkerId(_coordinates.length.toString());
int markerId = _coordinates.length + 1;
_coordinates.add(latLng);
_markers.add(Marker(
markerId: MarkerId(latLng.toString()),
// markerId,
position: latLng,
icon: BitmapDescriptor.defaultMarker,
draggable: true,
onDragEnd: (latLng) {
final markerList = _markers.toList();
final index = markerList.indexWhere((marker) => marker.markerId == markerId);
_coordinates[index] = latLng;
_updatePolygon();
},
infoWindow: InfoWindow(
title: "Marker ${_coordinates.length}",
snippet: "Tap to delete",
onTap: () => _onDeletePress(latLng, markerId),
),
));
_updatePolygon();
}
void _onDeletePress(LatLng latLng, markerId) {
// final markerList = _markers.toList();
// final index1 = markerList.indexWhere((marker) => marker.markerId == markerId);
// final index = _coordinates.indexWhere((element) => (element.longitude == latLng.longitude) && (element.latitude == latLng.latitude));
// setState(() {
// print("index: ${index}");
// print("cordinate length ${_coordinates.length.toString()}");
// _markers.remove(index1);
// _coordinates.removeAt(index);
// _polygons = Set.of(_polygons.toList()..removeAt(index));
// });
// final markerList = _markers.toList();
// int index = markerList.indexWhere((m) => m.markerId.value == markerId.toString());
// int index = _markers.indexWhere((m) => m.markerId.value == markerId.toString());
setState(() {
// print("index: ${index}");
print("markerId: ${markerId}");
print("cordinate length ${_coordinates.length.toString()}");
_coordinates.removeAt(markerId);
// _markers.removeWhere((m) => m.markerId.value == markerId);
_markers.removeWhere((marker) => marker.markerId.value == "myMarkerId");
_polygons = Set.from(_polygons.toList()..removeAt(markerId));
});
_updatePolygon();
}
void _updatePolygon() {
_polygons.clear();
print("cordinate length ${_coordinates.length.toString()}");
if (_coordinates.length >= 3) {
final origin = _coordinates.reduce((value, element) => LatLng(
(value.latitude + element.latitude) / 2,
(value.longitude + element.longitude) / 2));
final polarCoordinates =
_coordinates.map((c) => _toPolar(c, origin)).toList();
polarCoordinates.sort((a, b) =>
a.theta == b.theta ? a.r.compareTo(b.r) : a.theta.compareTo(b.theta));
final orderedCoordinates =
polarCoordinates.map((p) => _fromPolar(p, origin)).toList();
setState(() {
_polygons.clear();
_polygons.add(Polygon(
polygonId: PolygonId(_coordinates.length.toString()),
points: orderedCoordinates,
fillColor: Colors.green.withOpacity(0.5),
strokeWidth: 5,
strokeColor: Colors.green,
));
});
}
else {
setState(() {
_polygons.clear();
});
}
}
Thanks in advance for your help.

How to access widget in an initializer in google maps flutter

I am passing select latitude and longitude from google maps to my flutter app's another screen where I am calculating the distance between 2 locations.
I've got the values coming fine but there's this widget can't be accessed in an initializer issue coming.
I'm using google maps and I've to pass the widget.lat widget.long values to the userLocation marker.
I'm using this tutorial's code by the way
Get distance between locations
Here's my code
class CalculateDistance extends StatefulWidget {
const CalculateDistance({super.key, required this.lang, required this.lat});
final double lang;
final double lat;
#override
// ignore: library_private_types_in_public_api
_CalculateDistanceState createState() => _CalculateDistanceState();
}
class _CalculateDistanceState extends State<CalculateDistance> {
GoogleMapController? mapController; //contrller for Google map
PolylinePoints polylinePoints = PolylinePoints();
String googleAPiKey = "YOUR_API_KEY";
Set<Marker> markers = {}; //markers for google map
Map<PolylineId, Polyline> polylines = {}; //polylines to show direction
LatLng storeLocation =
const LatLng(-30.600164342582726, 23.508854043469647); // Store location
// This is where I can't use the passed values
LatLng userLocation = LatLng(widget.lat, widget.lang); // User location
double distance = 0.0;
#override
void initState() {
markers.add(Marker(
//add start location marker
markerId: MarkerId(storeLocation.toString()),
position: storeLocation, //position of marker
infoWindow: const InfoWindow(
//popup info
title: 'Store Location',
snippet: 'Store Marker',
),
icon: BitmapDescriptor.defaultMarker, //Icon for Marker
));
markers.add(Marker(
//add distination location marker
markerId: MarkerId(userLocation.toString()),
position: userLocation, //position of marker
infoWindow: const InfoWindow(
//popup info
title: 'User Location',
snippet: 'User Marker',
),
icon: BitmapDescriptor.defaultMarker, //Icon for Marker
));
getDirections(); //fetch direction polylines from Google API
super.initState();
}
getDirections() async {
List<LatLng> polylineCoordinates = [];
PolylineResult result = await polylinePoints.getRouteBetweenCoordinates(
googleAPiKey,
PointLatLng(storeLocation.latitude, storeLocation.longitude),
PointLatLng(userLocation.latitude, userLocation.longitude),
travelMode: TravelMode.driving,
);
if (result.points.isNotEmpty) {
for (var point in result.points) {
polylineCoordinates.add(LatLng(point.latitude, point.longitude));
}
} else {
print(result.errorMessage);
}
//polulineCoordinates is the List of longitute and latidtude.
double totalDistance = 0;
for (var i = 0; i < polylineCoordinates.length - 1; i++) {
totalDistance += calculateDistance(
polylineCoordinates[i].latitude,
polylineCoordinates[i].longitude,
polylineCoordinates[i + 1].latitude,
polylineCoordinates[i + 1].longitude);
}
print(totalDistance);
setState(() {
distance = totalDistance;
});
//add to the list of poly line coordinates
addPolyLine(polylineCoordinates);
}
addPolyLine(List<LatLng> polylineCoordinates) {
PolylineId id = const PolylineId("poly");
Polyline polyline = Polyline(
polylineId: id,
color: Colors.deepPurpleAccent,
points: polylineCoordinates,
width: 8,
);
polylines[id] = polyline;
setState(() {});
}
double calculateDistance(lat1, lon1, lat2, lon2) {
var p = 0.017453292519943295;
var a = 0.5 -
cos((lat2 - lat1) * p) / 2 +
cos(lat1 * p) * cos(lat2 * p) * (1 - cos((lon2 - lon1) * p)) / 2;
return 12742 * asin(sqrt(a));
}
// Scaffold ahead
Declare this LatLng with late keyword as a member of _CalculateDistanceState class:
class _CalculateDistanceState extends State<CalculateDistance> {
late LatLng _userLocation;
(...)
}
Then in the initState you will have access to the widget:
void initState() {
super.initState();
_userLocation = LatLng(widget.lat, widget.lang);
(...)
}

how can i get my category name when value type is id in dropdown flutter

i am trying to show category name and store category id in database but my categories id are not line by line
My Dropdown button
DropdownButton(
items: categoriesList.map((item) {
return DropdownMenuItem(
child: Text(item['name'].toString()), value: item['id'].toString());
}).toList(),
onChanged: (changedCategoryId) {
setState(() {
print(int.parse(changedCategoryId.toString()));
});
},
hint: Text(selectedCategory),
),
fetch data code
List categoriesList = [];
var selectedCategory = "Select Category";
#override
void initState() {
// TODO: implement initState
fetchCategories();
super.initState();
}
fetchCategories() async {
var fetchedCategoriesList =
await http.get(Uri.parse("${AppConfig.BASE_URL}categories"));
var decodedFetchedCategoriesList =
json.decode(fetchedCategoriesList.body)['data'];
setState(() {
categoriesList = decodedFetchedCategoriesList;
});
print(categoriesList);
}
onChanged: (changedCategoryId) { // this runType is Dropdow
final id = changedCategoryId['id'];
final name = changedCategoryId['name'];
},
you can store the id to database , and show the name on screen

How to use DropDownButton for dynamic list in flutter?

I am trying to implement dynamic dropdownButton in my app where the items of dropdown is going to come from the names of columns in my excel sheet. I am able to show all the columns of excel but I couldn't able to trace the index of column which the user is selecting from the dropdown.
I tried to make a map of dropdownitems like this in which the key is the index and value is the DropdownMenuItem like this :
late int selectedIndex; //where I want to store the selected index
late String initialDropDownVal;
List<Map<int,DropdownMenuItem<String>>> dropdownItems = [];
Then I added some values by iterating the columns of excel using a for loop :
excel = Excel.decodeBytes(bytes);
sheet = excel['Sheet1'];
for(int i = 1; i< sheet.maxCols; ++i){
var cell = sheet.cell(CellIndex.indexByColumnRow(rowIndex: 0, columnIndex: i));
String val = cell.value.toString();
if(val=="null"){
break;
}else{
if(i==1){
initialDropDownVal = val;
}
var newItem = DropdownMenuItem(
child: Text(val),
value: val,
);
dropdownItems.add({i:newItem});
}
}
But I could not able to map the values in items attribute of DropdownButton, I tried to implement like this but this is throwing error
DropdownButton(
value: selectedVal,
icon: const Icon(Icons.keyboard_arrow_down),
items: dropdownItems.map((int i,DropdownMenuItem<String> p) => p).toList(),
onChanged: (String? value){
setState(() {
initialDropDownVal = value!;
});
})
And I am not sure how to change set the selectedIndex in onChanged function. Please help me in this. Thanks
late int selectedIndex; //where I want to store the selected index
late String initialDropDownVal;
List<DropdownMenuItem<String>> dropdownItems = [];
excel = Excel.decodeBytes(bytes);
sheet = excel['Sheet1'];
for(int i = 1; i< sheet.maxCols; ++i){
var cell = sheet.cell(CellIndex.indexByColumnRow(rowIndex: 0, columnIndex: i));
String val = cell.value.toString();
if(val=="null"){
break;
}else{
if(i==1){
initialDropDownVal = val;
}
var newItem = DropdownMenuItem(
child: Text(val),
value: val,
);
dropdownItems.add(newItem);
}
}
DropdownButton(
value: selectedVal,
icon: const Icon(Icons.keyboard_arrow_down),
items: dropdownItems,
onChanged: (String? value){
setState(() {
initialDropDownVal = value!;
selectedIndex=dropdownItems.indexWhere((i)=>i.value==value);
});
})
late int selectedIndex; //where I want to store the selected index
late String initialDropDownVal;
List<Map<String,int>> dropdownItems = [];
excel = Excel.decodeBytes(bytes);
sheet = excel['Sheet1'];
for(int i = 1; i< sheet.maxCols; ++i){
var cell = sheet.cell(CellIndex.indexByColumnRow(rowIndex: 0, columnIndex: i));
String val = cell.value.toString();
if(val=="null"){
break;
}else{
if(i==1){
initialDropDownVal = val;
}
dropdownItems.add({val:i});
}
}
DropdownButton(
value: selectedVal,
icon: const Icon(Icons.keyboard_arrow_down),
items: dropdownItems.map((e) {
return DropdownMenuItem(
child: Text(e.keys.first),
value: e.keys.first,
);
}).toList(),
onChanged: (String? value){
setState(() {
initialDropDownVal = value!;
for(int i = 0; i< dropdownItems.length; ++i){
if(dropdownItems[i].keys.first==value){
setState(() {
selectedIndex = dropdownItems[i].values.first;
});
break;
}
}
});
}),
)

Flutter/Dart: Retrieve sqflite data and put into an array/list

I have exercises stored in my sqflite database as strings. I would like to retrieve the data and put it into a list or array called typeArray[]. I am trying to retrieve the data in a class that will use the list of exercises in functions to perform random workout generation. Therefore I need to retrieve the list before doing the calculations. The return values of these workout generator functions is then passed using MaterialPageRoute() to custom_workout_screen.dart where I use the return values of the functions to be displayed as strings to the screen.
I got it to work with predefined list ie where I had set the list = ['exerciseOne', 'exerciseTwo', 'etc']
How would I pull the data in a class that is not stateless or statefull? I am happy to do the work, but am confused in terms of the approach to the problem - any guidance appreciated!
Generates Custom Workout to be passed to the Custom Workout Screen: generate_custom.dart
class GenerateCustom {
int rnd;
// NEED TO SET typeArray to values from SQFLITE DB ?
List typeArray = [];
GenerateCustom({this.difficulty});
final int difficulty;
String workout;
String ex1;
String ex2;
String ex3;
String ex4;
String ex5;
String getStrengthType() {
var random = Random();
var i = random.nextInt(14);
print(typeArray[i]);
return typeArray[i];
}
String cuExerciseOne() {
if (difficulty == 1) {
workout = ('1: ' +
getStrengthType() +
' ' +
getRepsEasy() +
'x' +
getSetsEasy());
} else if (difficulty == 2) {
workout = ('1: ' +
getStrengthType() +
' ' +
getRepsMedium() +
'x' +
getSetsMedium());
} else {
workout = ('1: ' +
getStrengthType() +
' ' +
getRepsHard() +
'x' +
getSetsHard());
}
return workout;
}
DatabaseHelper: database_helper.dart
class DatabaseHelper {
static DatabaseHelper _databaseHelper;
static Database _database;
String exerciseTable = 'exercise_table';
String colId = 'id';
String title = 'title';
DatabaseHelper._createInstance();
factory DatabaseHelper() {
if (_databaseHelper == null) {
_databaseHelper = DatabaseHelper._createInstance();
}
return _databaseHelper;
}
Future<Database> get database async {
if (_database == null) {
_database = await initializeDatabase();
}
return _database;
}
Future<Database> initializeDatabase() async {
//get ios + android dir path
Directory directory = await getApplicationDocumentsDirectory();
String path = directory.path + 'exercise.db';
//Open or create db at path
var exerciseDatabase =
await openDatabase(path, version: 1, onCreate: _createDb);
return exerciseDatabase;
}
void _createDb(Database db, int newVersion) async {
await db.execute(
'CREATE TABLE $exerciseTable ($colId INTEGER PRIMARY KEY, $title TEXT)');
}
Future<List<Map<String, dynamic>>> getExerciseMapList() async {
Database db = await this.database;
var result = await db.rawQuery('SELECT * FROM $exerciseTable');
return result;
}
//Insert
Future<int> insertExercise(Exercise exercise) async {
Database db = await this.database;
var result = await db.insert(exerciseTable, exercise.toMap());
return result;
}
//Update
Future<int> updateExercise(Exercise exercise) async {
var db = await this.database;
var result = await db.update(exerciseTable, exercise.toMap(),
where: '$colId = ?', whereArgs: [exercise.id]);
debugPrint('update called');
return result;
}
//Delete
Future<int> deleteExercise(int id) async {
var db = await this.database;
int result =
await db.rawDelete('DELETE FROM $exerciseTable WHERE $colId = $id');
return result;
}
//get no of objects in db
Future<int> getCount() async {
Database db = await this.database;
List<Map<String, dynamic>> x =
await db.rawQuery('SELECT COUNT (*) from $exerciseTable');
int result = Sqflite.firstIntValue(x);
return result;
}
//get Map list from db, convert to Exercise List object
Future<List<Exercise>> getExerciseList() async {
//get map list and # of entries in db
var exerciseMapList = await getExerciseMapList();
int count = exerciseMapList.length;
List<Exercise> exerciseList = List<Exercise>();
//Loop to create exercise list from a map list
for (int i = 0; i < count; i++) {
exerciseList.add(Exercise.fromMapObject(exerciseMapList[i]));
}
return exerciseList;
}
}
Screen which uses output: custom_workout_screen.dart
class CustomWorkoutScreen extends StatelessWidget {
CustomWorkoutScreen(
this.customDifficulty,
this.customWorkout1,
this.customWorkout2,
this.customWorkout3,
this.customWorkout4,
this.customWorkout5);
final String customDifficulty;
final String customWorkout1;
final String customWorkout2;
final String customWorkout3;
final String customWorkout4;
final String customWorkout5;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: kBackgroundColour,
appBar: AppBar(
backgroundColor: kButtonAndBarColour,
title: Text('CUSTOM', style: kTitleStyle),
),
body: Center(
child: Column(
children: <Widget>[
SizedBox(height: 15.0),
Expanded(
child: Text(
'WORKOUT',
style: kTitleStyleDark,
),
),
Container(
child: Text(
'DIFFICULTY: ' + customDifficulty,
style: kTitleStyleDark,
),
),
SizedBox(
height: 25.0,
),
Container(
margin: EdgeInsets.only(bottom: 10.0),
child: Column(
children: <Widget>[
Text('READY...GO!', style: kCommentTextStyle),
Text(
customWorkout1,
style: kMainTextStyledDark,
),
Text('REST 30s!', style: kCommentTextStyle),
Text(
customWorkout2,
style: kMainTextStyledDark,
),
Text('REST 30s!', style: kCommentTextStyle),
Text(
customWorkout3,
style: kMainTextStyledDark,
),
Text('REST 30s!', style: kCommentTextStyle),
Text(
customWorkout4,
style: kMainTextStyledDark,
),
Text('REST 30s!', style: kCommentTextStyle),
Text(
customWorkout5,
style: kMainTextStyledDark,
),
Text('GOOD JOB, REST!', style: kCommentTextStyle),
Text('OR GO AGAIN?', style: kCommentTextStyle),
],
),
),
SizedBox(
height: 50.0,
),
BottomButton(
buttonTitle: 'NEW WORKOUT',
onTap: () {
Navigator.pop(context);
},
),
],
),
),
);
}
}
There are basically two approaches to your problem [as far as I know, probably more]. The first approach is by using Provider package. Below I will explain that approach in details:
First approach
Assuming that you already have your DatabaseHelper file you can define a class for your randomly picked workout [something like this, but not necessarily this way]. To simplify and focus on a concept I assume that Database has following fields: difficulty TEXT PRIMARY KEY, workout1 TEXT, workout2 TEXT, workout3 TEXT, workout4 TEXT, workout5 TEXT
import 'package:flutter/foundation.dart';
class RandomWorkout {
final String difficulty;
final String workout1;
final String workout2;
final String workout3;
final String workout4;
final String workout5;
RandomWorkout({
#required this.difficulty,
#required this.workout1,
#required this.workout2,
#required this.workout3,
#required this.workout4,
#required this.workout5,
});
}
Then you need to crate workout_provider.dart file to handle all provider related functions [adding, removing, fetching, but let use focus on fetching and setting]:
class WorkoutProvider with ChangeNotifier {
List<RandomWorkout> _workouts = [];
List<Task> get items {
return [..._tasks];
}
Future<void> fetchAndSetWorkouts() async {
final dataList =
await DBHelper.getExerciseMapList();
_workouts = dataList
.map(
(item) => RandomWorkout(
difficulty: item['difficulty'],
workout1: item['workout1'],
workout2: item['workout2'],
workout3: item['workout3'],
workout4: item['workout4'],
workout5: item['workout5'],
),
)
.toList();
notifyListeners();
}
}
In your main.dart file you will need to wrap your MaterialApp widget with Provider widget of your choice (MultiProvider/Provider).
Finally in your file where you plan to populate the data you can use FutureBuilder to asynchronously fetch data from the database with future being Provider.of<TaskProvider>(context, listen: false).fetchAndSetWorkouts() and using Consumer's builder to retrieve the items getter from workout_provider.dart.
This is probably a lot to grasp, so I would recommend you visiting Provider package's documentation page as well as examples on GitHub, there are plenty of people who could explain better than me
Second approach
It is a simpler method, but is less flexible and efficient. You just asynchronously use DatabaseHelper directly where you need data as follows:
Future<List<RandomWorkout>> fetchAndSetWorkouts() async {
final dataList =
await DBHelper.getExerciseMapList();
workouts = dataList
.map(
(item) => RandomWorkout(
difficulty: item['difficulty'],
workout1: item['workout1'],
workout2: item['workout2'],
workout3: item['workout3'],
workout4: item['workout4'],
workout5: item['workout5'],
),
)
.toList();
return workouts;
}