Flutter Isar stops working after hot restart - flutter

I started learning flutter isar yesterday and I couldn't love it more. I created a demo app with it and for some reason, it is not working as expected.
The app has two sections: Original(This contain the dummyData) and the Database(this contains data in the isar database).
When an item is starred in the original, it is added in the database and the icon is changed to filled_star. When the item is unstarred in the original section, it is expected to be removed from the database section and the icon is expected to change to star_outline. This is works fine.
However, when the app is hot-restarted, I am unable to unstar the items. Check the GIF below.
main.dart
import 'package:flutter/material.dart';
import 'package:isardemo/isar_files/course.dart';
import 'package:isardemo/isar_files/isar.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return ProviderScope(
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
brightness: Brightness.dark,
primarySwatch: Colors.blueGrey,
),
home: const MyHomePage(),
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final dummyData = [
Course()
..title = 'MTH 212'
..courseId = '1ab',
Course()
..title = 'STS 432'
..courseId = '2bc',
Course()
..title = 'SHS 555'
..courseId = '3de',
Course()
..title = 'HMM 999'
..courseId = '4fg',
Course()
..title = 'EEE 666'
..courseId = '5hi',
];
Future<void> onFavTap(IsarService courseData, Course course) async {
if (await courseData.isItemDuplicate(course)) {
await courseData.deleteCourse(course);
setState(() {});
debugPrint('${course.courseId} deleted');
} else {
await courseData.addCourse(course);
setState(() {});
debugPrint('${course.courseId} added');
}
}
final courseData = IsarService();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Isar'),
),
body: ListView(
padding: const EdgeInsets.all(20),
children: [
Center(
child: FutureBuilder(
initialData: courseData,
future: courseData.favoritesCount(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return Text(
snapshot.data.toString(),
style:
const TextStyle(fontSize: 25, color: Colors.lightGreen),
);
} else {
return const LinearProgressIndicator();
}
},
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () {
courseData.cleanDb();
setState(() {});
},
child: const Text('Destroy Database')),
),
const Text('Original',
style: TextStyle(fontSize: 30, color: Colors.green)),
ListView.separated(
shrinkWrap: true,
separatorBuilder: (context, index) => const SizedBox(height: 5),
itemCount: dummyData.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
tileColor: Colors.blueGrey,
title: Text(dummyData[index].title),
trailing: IconButton(
icon: FutureBuilder(
// initialData: courseData.isItemDuplicate(dummyData[index]),
future: courseData.isItemDuplicate(dummyData[index]),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data) {
return const Icon(Icons.star);
} else {
return const Icon(Icons.star_border_outlined);
}
}
return const Icon(Icons.g_mobiledata,
color: Colors.green);
},
),
onPressed: () => onFavTap(courseData, dummyData[index])),
);
},
),
const SizedBox(height: 20),
const Text(
'database',
style: TextStyle(fontSize: 30, color: Colors.green),
),
FutureBuilder(
future: courseData.getAllCourses(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data!.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(snapshot.data![index].title),
trailing: InkWell(
onTap: () async {
await courseData
.deleteCourse(snapshot.data![index]);
setState(() {});
},
child: const Icon(Icons.star)),
);
},
);
} else {
return const Center(
child: LinearProgressIndicator(),
);
}
}),
],
),
);
}
}
course.dart
import 'package:isar/isar.dart';
part 'course.g.dart';
#Collection()
class Course {
Id id = Isar.autoIncrement;
late String courseId;
late String title;
late bool isFavorite = false; // new property
}
isar.dart
import 'package:path_provider/path_provider.dart';
import 'course.dart';
class IsarService {
late Future<Isar> _db;
IsarService() {
_db = openIsar();
}
Future<Isar> openIsar() async {
if (Isar.instanceNames.isEmpty) {
final directory = await getApplicationDocumentsDirectory();
return await Isar.open([CourseSchema],
inspector: true, directory: directory.path);
} else {
return await Future.value(Isar.getInstance());
}
}
Future<void> addCourse(Course course) async {
final isar = await _db;
await isar.writeTxn(() async {
await isar.courses.put(course);
});
}
Future<bool> isItemDuplicate(Course course) async {
final isar = await _db;
final count =
await isar.courses.filter().courseIdContains(course.courseId).count();
return count > 0;
}
Future<List<Course>> getAllCourses() async {
final isar = await _db;
return isar.courses.where().findAll();
}
Future<void> deleteCourse(Course course) async {
final isar = await _db;
await isar.writeTxn(() async {
await isar.courses.delete(course.id);
});
}
Future<String> favoritesCount() async {
final isar = await _db;
final count = await isar.courses.count();
return count.toString();
}
Future<void> cleanDb() async {
final isar = await _db;
await isar.writeTxn(() => isar.clear());
}
}
I tried downgrading the isar version but it didn't work.

I fixed it! Instead of auto-incrementing the id, I used the Course's id instead. But Id expects an integer so I had to convert the Course's id into an integer using the fastHash.
Learn more
course.dart
#Collection()
class Course {
late String id;
Id get courseId => fastHash(id);
late String title;
}
int fastHash(String string) {
var hash = 0xcbf29ce484222325;
var i = 0;
while (i < string.length) {
final codeUnit = string.codeUnitAt(i++);
hash ^= codeUnit >> 8;
hash *= 0x100000001b3;
hash ^= codeUnit & 0xFF;
hash *= 0x100000001b3;
}
return hash;
}

Related

Racing problem of async function in Flutter

I have a Flutter page that makes use of 2 data sources: one from API (Internet) and one from Shared Preferences. The API source has no problem, as I used FutureBuilder in the build() method. For the Shared Preferences, I have no idea how to apply another Future Builder (or should I add one more?). Here are the codes (I tried to simplify them):
Future<List<City>> fetchCities(http.Client client) async {
final response = await client
.get(Uri.parse('https://example.com/api/'));
return compute(parseCities, response.body);
}
List<City> parseCities(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<City>((json) => City.fromJson(json)).toList();
}
class CityScreen extends StatelessWidget {
static const routeName = '/city';
const CityScreen({super.key, required this.title});
final String title;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: FutureBuilder<List<City>>(
future: fetchCities(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(snapshot.error.toString()),
);
} else if (snapshot.hasData) {
return CityList(cities: snapshot.data!);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
)
);
}
}
class CityList extends StatefulWidget {
const CityList({super.key, required this.cities});
final List<City> cities;
#override
State<CityList> createState() => _CityListState();
}
class _CityListState extends State<CityList> {
List<String> completedMissionIDs = [];
#override
void initState() {
super.initState();
Player.loadMissionStatus().then((List<String> result) {
setState(() {
completedMissionIDs = result;
if (kDebugMode) {
print(completedMissionIDs);
}
});
});
}
#override
Widget build(BuildContext context) {
return ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: widget.cities.length * 2,
itemBuilder: (context, i) {
if (i.isOdd) return const Divider();
final index = i ~/ 2;
double completedPercent = _calculateCompletionPercent(widget.cities[index].missionIDs, completedMissionIDs);
return ListTile(
leading: const Icon(Glyphicon.geo, color: Colors.blue),
title: Text(widget.cities[index].cityName),
trailing: Text('$completedPercent%'),
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => MissionScreen(title: '${widget.cities[index].cityName} Missions', cityId: widget.cities[index].id),
)
);
},
);
},
);
}
double _calculateCompletionPercent<T>(List<T> cityMissionList, List<T> completedList) {
if(cityMissionList.isEmpty) {
return 0;
}
int completedCount = 0;
for (var element in completedList) {
if(cityMissionList.contains(element)) {
completedCount++;
}
}
if (kDebugMode) {
print('Completed: $completedCount, Total: ${cityMissionList.length}');
}
return completedCount / cityMissionList.length;
}
}
The problem is, the build function in the _CityListState loads faster than the Player.loadMissionStatus() method in the initState, which loads a List<int> from shared preferences.
The shared preferences are loaded in the midway of the ListTiles are generated, making the result of completedPercent inaccurate. How can I ask the ListTile to be built after the completedPercent has been built?
Thanks.
I would start by making CityList a StatelessWidget that accepts completedMissionIDs as a constructor parameter.
Your CityScreen widget can call both APIs and combine the results into a single Future. Pass the combined Future to your FutureBuilder. That way you can render the CityList once all of the data has arrived from both APIs.
I put together a demo below:
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(
home: CityScreen(title: 'City Screen'),
));
}
class CombinedResult {
final List<City> cities;
final List<int> status;
const CombinedResult({
required this.cities,
required this.status,
});
}
class City {
final String cityName;
final List<int> missionIDs;
const City(this.cityName, this.missionIDs);
}
class Player {
static Future<List<int>> loadMissionStatus() async {
await Future.delayed(const Duration(seconds: 1));
return [0, 3];
}
}
Future<List<City>> fetchCities() async {
await Future.delayed(const Duration(seconds: 2));
return const [
City('Chicago', [1, 2, 3, 4]),
City('Helsinki', [1, 2, 3, 4]),
City('Kathmandu', [0, 4]),
City('Seoul', [1, 2, 3]),
];
}
class CityScreen extends StatefulWidget {
const CityScreen({super.key, required this.title});
final String title;
#override
State<CityScreen> createState() => _CityScreenState();
}
class _CityScreenState extends State<CityScreen> {
late Future<CombinedResult> _future;
#override
void initState() {
super.initState();
_future = _fetchData();
}
Future<CombinedResult> _fetchData() async {
final cities = await fetchCities();
final status = await Player.loadMissionStatus();
return CombinedResult(cities: cities, status: status);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: FutureBuilder<CombinedResult>(
future: _future,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(snapshot.error.toString()),
);
} else if (snapshot.hasData) {
return CityList(
cities: snapshot.data!.cities,
completedMissionIDs: snapshot.data!.status,
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
}
class CityList extends StatelessWidget {
const CityList({
super.key,
required this.cities,
required this.completedMissionIDs,
});
final List<City> cities;
final List<int> completedMissionIDs;
#override
Widget build(BuildContext context) {
return ListView.separated(
padding: const EdgeInsets.all(16.0),
itemCount: cities.length,
separatorBuilder: (context, i) => const Divider(),
itemBuilder: (context, i) => ListTile(
leading: const Icon(Icons.location_city, color: Colors.blue),
title: Text(cities[i].cityName),
trailing: Text(
'${_calculateCompletionPercent(cities[i].missionIDs, completedMissionIDs)}%'),
),
);
}
double _calculateCompletionPercent<T>(
List<T> cityMissionList, List<T> completedList) =>
completedList.where(cityMissionList.contains).length /
cityMissionList.length;
}
First of all I would separate the data layer from the presentation. Bloc would be one example.
To combine 2 Futures you could do something like
final multiApiResult = await Future.wait([
sharedPrefs.get(),
Player.loadMissionStatus()
])

How to use a async function inside a ListView.builder?

Is there anyway to get a future when displaying a list?
I have list with two user id's ( one of them is the userlogged in, the other one is the user to chat with ).
my goal is to get and display the other users name in the chat.
the issue I am running into, is that you cant do async functions within the list
how can i fix this?
typedef JobCallback = void Function(CloudChat job);
class ChatsListWidget extends StatelessWidget {
final Iterable<CloudChat> cloudChats; // list of jobs
final JobCallback onDeleteJob;
final JobCallback onTap;
String get userId => AuthService.firebase().currentUser!.id;
const ChatsListWidget({
Key? key,
required this.cloudChats,
required this.onDeleteJob,
required this.onTap,
}) : super(key: key);
Future getOtherUsersName(userIdsArr) async {
String? otherUserInChatId;
for (var _userId in userIdsArr) {
if (_userId != userId) {
otherUserInChatId = _userId;
}
}
var userData = await FirebaseFirestore.instance
.collection('user')
.doc(otherUserInChatId)
.get();
return userData[userFirstNameColumn];
}
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: cloudChats.length,
itemBuilder: (context, index) {
final job = cloudChats.elementAt(index);
var otherUserName = await getOtherUsersName(job.userIdsArr);
;
return ListTile(
onTap: () {
onTap(job);
},
title: Text(
otherUserName,
maxLines: 1,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
trailing: IconButton(
onPressed: () async {
//
},
icon: const Icon(Icons.delete),
),
);
},
);
}
}
I tried the following code as suggested but did not work::
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import '../../services/auth/auth_service.dart';
import '../../services/cloud/cloud_chat.dart';
import '../../services/cloud/cloud_storage_constants.dart';
typedef JobCallback = void Function(CloudChat job);
class ChatsListWidget extends StatelessWidget {
final Iterable<CloudChat> cloudChats; // list of jobs
final JobCallback onDeleteJob;
final JobCallback onTap;
String get userId => AuthService.firebase().currentUser!.id;
const ChatsListWidget({
Key? key,
required this.cloudChats,
required this.onDeleteJob,
required this.onTap,
}) : super(key: key);
Future getOtherUsersName(userIdsArr) async {
String? otherUserInChatId;
for (var _userId in userIdsArr) {
if (_userId != userId) {
otherUserInChatId = _userId;
}
}
var userData = await FirebaseFirestore.instance
.collection('user')
.doc(otherUserInChatId)
.get();
return userData[userFirstNameColumn];
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getOtherUsersName(job.userIdsArr),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: cloudChats.length,
itemBuilder: (context, index) {
final job = cloudChats.elementAt(index);
return ListTile(
onTap: () {
onTap(job);
},
title: Text(
otherUserName,
maxLines: 1,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
trailing: IconButton(
onPressed: () async {
//
},
icon: const Icon(Icons.delete),
),
);
},
);
} else if (snapshot.connectionState == ConnectionState.waiting) {
Center(
child: CircularProgressIndicator(),
);
}
return Center(
child: Text("Empty"),
);
},
);
}}
The simple and short answer is to use FutureBuilder which take future as named parameter and give you the result in builders parameters generally know as snapshot.
First of all wrap your list view with future builder :
FutureBuilder(
future: "you future funtion here",
builder: (context, snapshot) {
return ListView.builder(itemBuilder: itemBuilder);
}
)
now you can give future funtion , in your case it is
FutureBuilder(
future: getOtherUsersName(job.userIdsArr),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: cloudChats.length,
itemBuilder: (context, index) {
final job = cloudChats.elementAt(index);
return ListTile(
onTap: () {
onTap(job);
},
title: Text(
otherUserName,
maxLines: 1,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
trailing: IconButton(
onPressed: () async {
//
},
icon: const Icon(Icons.delete),
),
);
},
);
} else if (snapshot.connectionState == ConnectionState.waiting) {
Center(
child: CircularProgressIndicator(),
);
}
return Center(
child: Text("Empty"),
);
},
);
And remember to use snapshot if conditions for loading and empty data
complete code
import 'package:flutter/material.dart';
typedef JobCallback = void Function(CloudChat job);
class ChatsListWidget extends StatelessWidget {
final Iterable<CloudChat> cloudChats; // list of jobs
final JobCallback onDeleteJob;
final JobCallback onTap;
String get userId => AuthService.firebase().currentUser!.id;
const ChatsListWidget({
Key? key,
required this.cloudChats,
required this.onDeleteJob,
required this.onTap,
}) : super(key: key);
Future getOtherUsersName(userIdsArr) async {
String? otherUserInChatId;
for (var _userId in userIdsArr) {
if (_userId != userId) {
otherUserInChatId = _userId;
}
}
var userData = await FirebaseFirestore.instance
.collection('user')
.doc(otherUserInChatId)
.get();
return userData[userFirstNameColumn];
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getOtherUsersName(job.userIdsArr),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: cloudChats.length,
itemBuilder: (context, index) {
final job = cloudChats.elementAt(index);
return ListTile(
onTap: () {
onTap(job);
},
title: Text(
otherUserName,
maxLines: 1,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
trailing: IconButton(
onPressed: () async {
//
},
icon: const Icon(Icons.delete),
),
);
},
);
} else if (snapshot.connectionState == ConnectionState.waiting) {
Center(
child: CircularProgressIndicator(),
);
}
return Center(
child: Text("Empty"),
);
},
);
}}
HopeFully It will help you.

How to do incrementing and decrementing of a particular product in flutter

I'm working on a food delivery app I've tried to make an increment decrement system of a particular product in a list. At the start it works i.e the counter increases but a bit after the counter automatically return to 0 without any button press. I don't know why it's happening
Below is the code I'm trying
This is the class
class ItemData {
final String itemName;
final String itemPrice;
final String image;
int counter = 0;
bool isAdded = false;
ItemData({this.itemName, this.itemPrice, this.image});
}
This is the function for getting data from url
Future<List<ItemData>> _getProducts() async {
var data = await http
.get("https://orangecitycafe.in/app_configs/products_display.php");
var jsonData = json.decode(data.body);
List<ItemData> details = [];
for (var p in jsonData) {
ItemData detail = ItemData(
itemName: p["product_name"],
itemPrice: p["product_price"],
image: p["product_image"]);
details.add(detail);
}
return details;
}
This is the code for fetched products inside future builder
Widget _myCart() {
return FutureBuilder(
future: _getProfile(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(snapshot.data[index].itemName),
leading: Image.network("https://www.orangecitycafe.in/" +
snapshot.data[index].image),
trailing: snapshot.data[index].isAdded
? Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(Icons.remove),
onPressed: () {
setState(() {
if (snapshot.data[index].counter > 0) {
snapshot.data[index].counter--;
}
});
},
color: Colors.green,
),
Text(snapshot.data[index].counter.toString()),
IconButton(
icon: Icon(Icons.add),
color: Colors.green,
onPressed: () {
setState(() {
snapshot.data[index].counter++;
});
},
),
],
)
: RaisedButton(
onPressed: (){
setState(() {
snapshot.data[index].isAdded = true;
});
},
child: Text("Add"),
),
);
},
);
} else {
return Container();
}
},
);
}
The rest is working but only when I increase the counter it increases and after sometime it automatically returns to 0
You can copy paste run full code below
You can use the following way to use Future in FutureBuilder to avoid setState cause FutureBuilder rebuild again.
Detail reason https://github.com/flutter/flutter/issues/11426#issuecomment-414047398
didUpdateWidget of the FutureBuilder state is being called every time a rebuild is issued. This function checks if the old future object is different from the new one, and if so, refires the FutureBuilder.
To get past this, we can call the Future somewhere other than in the build function. For example, in the initState, and save it in a member variable, and pass this variable to the FutureBuilder.
code snippet
Future<List<ItemData>> _future;
...
#override
void initState() {
_future = _getProducts();
super.initState();
}
...
Widget _myCart() {
return FutureBuilder(
future: _future,
working demo
full code
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class ItemData {
final String itemName;
final String itemPrice;
final String image;
int counter = 0;
bool isAdded = false;
ItemData({this.itemName, this.itemPrice, this.image});
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Future<List<ItemData>> _future;
Future<List<ItemData>> _getProducts() async {
var data = await http
.get("https://orangecitycafe.in/app_configs/products_display.php");
var jsonData = json.decode(data.body);
List<ItemData> details = [];
for (var p in jsonData) {
ItemData detail = ItemData(
itemName: p["product_name"],
itemPrice: p["product_price"],
image: p["product_image"]);
details.add(detail);
}
return details;
}
#override
void initState() {
_future = _getProducts();
super.initState();
}
Widget _myCart() {
return FutureBuilder(
future: _future,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(snapshot.data[index].itemName),
leading: Image.network("https://www.orangecitycafe.in/" +
snapshot.data[index].image),
trailing: snapshot.data[index].isAdded
? Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(Icons.remove),
onPressed: () {
setState(() {
if (snapshot.data[index].counter > 0) {
snapshot.data[index].counter--;
}
});
},
color: Colors.green,
),
Text(snapshot.data[index].counter.toString()),
IconButton(
icon: Icon(Icons.add),
color: Colors.green,
onPressed: () {
setState(() {
snapshot.data[index].counter++;
});
},
),
],
)
: RaisedButton(
onPressed: () {
setState(() {
snapshot.data[index].isAdded = true;
});
},
child: Text("Add"),
),
);
},
);
} else {
return Container();
}
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: _myCart());
}
}

Use return from a Future <String> as the title of the alert widget

In my flutter project, i have a method that returns a String from the firebase:
Future<String> getNomeById(bool retirada, String userId) async {
QuerySnapshot snapshot = await firestore
.collection('users')
.where(FieldPath.documentId, isEqualTo: userId)
.getDocuments();
users = snapshot.documents.map((d) => User.fromDocument(d)).toList();
if (retirada) {
name = users[0].name;
} else {
name = 'Other';
}
return name;
}
Here I get the method return
u.getNomeById(retirada, userId).then((value) {
returnFutureString = value;
print(returnFutureString);//It's OK here
});
I need to use the return Future String in the title of my alertDialog,
I can't because my variable is null there, I know I'm doing it wrong, but I couldn't make it work by searching for similar examples.
class ExportAddressDialog extends StatelessWidget {
ExportAddressDialog(this.address, this.retirada, this.userId);
final Firestore firestore = Firestore.instance;
final Address address;
final bool retirada;
final String userId;
final ScreenshotController screenshotController = ScreenshotController();
#override
Widget build(BuildContext context) {
String returnFutureString;
Util u = new Util();
u.getNomeById(retirada, userId).then((value) {
returnFutureString = value;
print(returnFutureString);//It's OK here
});
return AlertDialog(
title: Text(returnFutureString),//I need to use my returnFutureString as the alert title, but is null here
content: Screenshot(
controller: screenshotController,
child: Container(
padding: const EdgeInsets.all(8),
color: Colors.white,
child: Text(
'${address.street}, ${address.number} ${address.complement}\n'
'${address.district}\n'
'${address.city}/${address.state}\n'
'${address.zipCode}',
),
),
),
contentPadding: const EdgeInsets.fromLTRB(16, 16, 16, 0),
actions: <Widget>[
FlatButton(
onPressed: () async {
Navigator.of(context).pop();
final file = await screenshotController.capture();
await GallerySaver.saveImage(file.path);
},
textColor: Theme.of(context).primaryColor,
child: const Text('Exportar'),
)
],
);
}
Future<String> _getTitle() async {
String returnFutureString = await u.getNomeById(retirada, userId)
return returnFutureString;
}
Use FutureBuilder to fetch async values:
FutureBuilder<String>(
future: _getTitle(),
builder: (ctx, snapshot) {
if (snapshot.hasData) {
return AlertDialog(
title: Text(snapshot.data)
);
}
return Center(child: CircularProgressIndicator());
}
),);

problem putting progressDialog.shide () in flutter

I am new to flutter and I am having the following problem.
I am trying to use the progressDialog in in a listview, I make the query to my database, I extract the list and pass it to the listview, I am trying to use the progressDialog so when I start loading the list it will run and tell the user to wait, and when I finish loading the list then the progressDialog is hidden, so far it works for me by bringing me the list and the progressDialog is executed saying to wait, but when I put the progressDialog.hide where the loading of the list ends I this is not accepting that line of code (progressDialog .hidde)
image:
enter image description here
import 'dart:convert';
import 'package:fluterproyecto1/Modulos/DetalleUser.dart';
import 'package:flutter/material.dart';
import 'package:progress_dialog/progress_dialog.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
String username2 = '';
String profesion = '';
String name = '';
class MemberPage extends StatefulWidget {
MemberPage({Key key}) : super(key: key);
#override
_MemberPageState createState() => _MemberPageState();
}
class _MemberPageState extends State<MemberPage> {
Map data;
List userData;
ProgressDialog progressDialog;
String name = '';
Future getData() async {
http.Response response =
await http.get("http://masciudad.com.co/flutter/getdata.php");
data = json.decode(response.body);
//setState(() {
//progressDialog.show();
userData = data["data"];
//progressDialog.hide();
//});
}
#override
void initState() {
super.initState();
getData();
}
#override
Widget build(BuildContext context) {
progressDialog = ProgressDialog(context, type: ProgressDialogType.Normal);
progressDialog.style(message: 'Por favor espere...');
progressDialog.show();
setState(() {
obtenerPreferencias();
});
return Scaffold(
appBar: AppBar(
title: Text("Bienvenido $username2"),
),
body: ListView.builder(
itemCount: userData == null ? 0 : userData.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
children: <Widget>[
Image.asset(
"assets/128.jpg",
width: 30.0,
height: 30.0,
),
//CircleAvatar(
///cuando la imagen es de interntet
//backgroundImage: NetworkImage(
// "https://s3.amazonaws.com/uifaces/faces/twitter/follettkyle/128.jpg"),
//),
Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
"${userData[index]["username"]} - ${userData[index]["profesion"]}",
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w700,
),
),
)
],
),
),
onTap: () => Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) =>
new DetalleUser(name: userData[index]["username"]))),
);
},
),
//Navigator.of(context).push(MaterialPageRoute(
// builder: (BuildContext context) => MyHomePage()));
//Navigator.pushReplacementNamed(context, "/MyHomePage");
);
}
Future obtenerPreferencias() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
setState(() {
username2 = preferences.get("username2") ?? "";
profesion = preferences.get("profesion") ?? "";
});
}
Future destruirPreferencias() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
preferences.clear();
}
}
You can copy paste run full code below
You can use addPostFrameCallback and put logical in another function getRelatedData()
code snippet
void getRelatedData() async {
progressDialog = ProgressDialog(context, type: ProgressDialogType.Normal);
progressDialog.style(message: 'Por favor espere...');
progressDialog.show();
await getData();
await obtenerPreferencias();
progressDialog.hide();
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
getRelatedData();
});
}
working demo
full code
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:progress_dialog/progress_dialog.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
String username2 = '';
String profesion = '';
String name = '';
class MemberPage extends StatefulWidget {
MemberPage({Key key}) : super(key: key);
#override
_MemberPageState createState() => _MemberPageState();
}
class _MemberPageState extends State<MemberPage> {
Map data;
List userData;
ProgressDialog progressDialog;
String name = '';
Future getData() async {
http.Response response =
await http.get("http://masciudad.com.co/flutter/getdata.php");
data = json.decode(response.body);
//setState(() {
//progressDialog.show();
userData = data["data"];
print("getData Done");
//progressDialog.hide();
//});
}
void getRelatedData() async {
progressDialog = ProgressDialog(context, type: ProgressDialogType.Normal);
progressDialog.style(message: 'Por favor espere...');
progressDialog.show();
await getData();
await obtenerPreferencias();
progressDialog.hide();
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
getRelatedData();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Bienvenido $username2"),
),
body: ListView.builder(
itemCount: userData == null ? 0 : userData.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
children: <Widget>[
Image.network(
"https://picsum.photos/250?image=9",
width: 30.0,
height: 30.0,
),
//CircleAvatar(
///cuando la imagen es de interntet
//backgroundImage: NetworkImage(
// "https://s3.amazonaws.com/uifaces/faces/twitter/follettkyle/128.jpg"),
//),
Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
"${userData[index]["username"]} - ${userData[index]["profesion"]}",
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w700,
),
),
)
],
),
),
onTap: () {
/*Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) =>
new DetalleUser(name: userData[index]["username"])));*/
});
},
),
//Navigator.of(context).push(MaterialPageRoute(
// builder: (BuildContext context) => MyHomePage()));
//Navigator.pushReplacementNamed(context, "/MyHomePage");
);
}
Future obtenerPreferencias() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
setState(() {
username2 = preferences.get("username2") ?? "";
profesion = preferences.get("profesion") ?? "";
});
}
Future destruirPreferencias() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
preferences.clear();
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MemberPage(),
);
}
}
Dear #Yeimer I would suggest read documentation carefully
https://pub.dev/packages/progress_dialog
Add the Package this
dependencies:
progress_dialog: ^1.2.4
1
Initialize your ProgressDialog object
final ProgressDialog prDialog = ProgressDialog(context);
//For normal dialog
prDialog = ProgressDialog(context,type: ProgressDialogType.Normal, isDismissible: true/false, showLogs: true/false);
2 Showing the progress dialog
await pr.show();
3
Dismissing the progress dialog
prDialog.hide().then((isHidden) {
print(isHidden);
});
// or simply
await prDialog.hide();