How to resolve flutter Error: Getter not found: 'widget? - flutter

I am creating Stateful Flutter Class Its Like below:
class Buildings extends StatefulWidget {
const Buildings(this._auth, {Key key, this.title}) : super(key: key);
final FirebaseAuth _auth;
final String title;
#override
_BuildingsState createState() => _BuildingsState();
}
class _BuildingsState extends State<Buildings> {
//////
}
Even though I am following constructor and Create State Method, I am now able to see widget object in **_BuildingsState** class. I cannot access **widget** variable. then forward In would like to get widget._auth.
How can I get widget variable. Currely I am getting :
Error: Getter not found: 'widget
Code Where I am trying to access widget:
Navigator.of(context).push(new MaterialPageRoute(
builder: (BuildContext context) =>
Rooms(widget._auth, widget.existingBuilding)));
Navigator.of(context).push(route);
Adding Complete File for reference: ( Basically cannot get widget in child components. I am not getting widget via incomingWidget object as well. for example, I am not getting incomingWidget._auth or widget._auth, any ways I mean )
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_login_page/AddBuilding.dart';
import 'package:flutter_login_page/Building.dart';
import 'package:flutter_login_page/EditBuilding.dart';
import 'package:flutter_login_page/EditRoom.dart';
import 'package:flutter_login_page/Home2.dart';
import 'package:flutter_login_page/Rooms.dart';
class Buildings extends StatefulWidget {
const Buildings(this._auth, this.existingBuilding, {Key key, this.title})
: super(key: key);
final FirebaseAuth _auth;
final Building existingBuilding;
final String title;
#override
_BuildingsState createState() => _BuildingsState();
}
class _BuildingsState extends State<Buildings> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Buildings'),
),
//appBar: AppBar(title: Text('ListViews')),
floatingActionButtonLocation: FloatingActionButtonLocation.endTop,
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
backgroundColor: Colors.green,
onPressed: () {
// setState(() {
// // i++;
// });
Navigator.pushNamed(context, 'add_building_screen');
},
),
body: BodyLayout(widget._auth, widget.existingBuilding),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.help),
label: 'Help',
),
BottomNavigationBarItem(
icon: Icon(Icons.logout),
label: 'Logout',
),
],
currentIndex: 0,
selectedItemColor: Colors.amber[800],
onTap: (value) {
// FirebaseAuth.
// Firebase
if (value == 0) {
print(value);
print("** loading home");
Navigator.of(context).push(
new MaterialPageRoute(
builder: (BuildContext context) =>
new HomePageScreen("Landge Rent Manager", widget._auth)),
);
}
if (value == 1) {
print(value);
print("** help");
widget._auth.signOut();
}
if (value == 2) {
print(value);
print("** logging out");
widget._auth.signOut();
Navigator.pushNamed(context, 'login_screen');
}
},
),
);
}
}
class BodyLayout extends StatefulWidget {
//FirebaseFirestore _firestore = FirebaseFirestore.instance;
final FirebaseAuth _auth;
final Building existingBuilding;
const BodyLayout(this._auth, this.existingBuilding, {Key key})
: super(key: key);
#override
_BodyLayoutState createState() => _BodyLayoutState();
}
class _BodyLayoutState extends State<BodyLayout> {
#override
Widget build(BuildContext context) {
return _myListView(context, widget);
}
}
void deleteBuilding(Building building) async {
print(">>> id for deleteis " + building.id.toString());
try {
final existingDoc = FirebaseFirestore.instance
.collection("Buildings")
.doc(building.id.toString());
return await existingDoc.set({
'name': "${building.name}",
'address': "${building.address}",
"isActive": false
});
} on Exception catch (e) {
print(e);
}
}
// replace this function with the code in the examples
Widget _myListView(BuildContext context, Widget incomingWidget) {
return StreamBuilder(
stream: FirebaseFirestore.instance
.collection('Buildings')
.where("isActive", isEqualTo: true)
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Container(child: Center(child: Text("No data")));
} else {
final buildings = snapshot.data.docs;
return ListView.builder(
itemCount: buildings.length,
itemBuilder: (context, index) {
return Card(
// <-- Card widget
child: ListTile(
leading: Icon(Icons.home),
title: Text(buildings[index].data()["name"] != null
? buildings[index].data()["name"]
: "-"),
subtitle: Column(
children: <Widget>[
Container(
child: Row(
children: <Widget>[
FlatButton(
child: Text("View Rooms"),
onPressed: () {
var id = buildings[index].reference.id != null
? buildings[index].reference.id
: "";
var name = buildings[index].data()["name"] != null
? buildings[index].data()["name"]
: "";
var address =
buildings[index].data()["address"] != null
? buildings[index].data()["address"]
: "";
Building currentBuilding =
new Building(id, name, "", address);
Navigator.of(context).push(new MaterialPageRoute(
builder: (BuildContext context) => new Rooms(
incomingWidget._auth,
incomingWidget.existingBuilding)));
},
),
FlatButton(
child: Text("Edit"),
onPressed: () {
var id = buildings[index].reference.id != null
? buildings[index].reference.id
: "";
var name = buildings[index].data()["name"] != null
? buildings[index].data()["name"]
: "";
var address =
buildings[index].data()["address"] != null
? buildings[index].data()["address"]
: "";
Building currentBuilding =
new Building(id, name, "", address);
var route = new MaterialPageRoute(
builder: (BuildContext context) =>
new EditBuilding(
widget._auth, widget.existingBuilding),
);
Navigator.of(context).push(new MaterialPageRoute(
builder: (BuildContext context) => Rooms(
widget._auth, widget.existingBuilding)));
Navigator.of(context).push(route);
// Navigator.pushNamed(
// context, 'edit_building_screen');
},
),
FlatButton(
child: Text("Delete"),
onPressed: () {
var id = buildings[index].reference.id != null
? buildings[index].reference.id
: "";
var name = buildings[index].data()["name"] != null
? buildings[index].data()["name"]
: "";
var address =
buildings[index].data()["address"] != null
? buildings[index].data()["address"]
: "";
Building currentBuilding =
new Building(id, name, "", address);
deleteBuilding(currentBuilding);
},
),
],
))
],
),
onTap: () {
Navigator.pushNamed(context, 'rooms_screen');
},
),
);
},
);
}
});
}

Related

how to implement code from ListView builder to Stickey group list when list item contains functions in flutter

I have a listview builder with TransactionCard as a listview item,
here I have used long press and ontap function for showing action of delete and edit on appbar.
Now I want to implement my code to StickyGroupList so that transactions can be shown in group by date...but I don't know how to deal with function what is used for transaction card as it is based on index....so how to use index in Sticky Group list
here is my code
class ShowAllTransactionScreen extends StatefulWidget {
const ShowAllTransactionScreen({Key? key}) : super(key: key);
#override
State<ShowAllTransactionScreen> createState() =>
_ShowAllTransactionScreenState();
}
class _ShowAllTransactionScreenState extends State<ShowAllTransactionScreen> {
bool islongpressed = false;
int? selectedindex;
TransactionModel? selectedtransaction;
bool showit = true;
final provider = Get.put(TransactionProvider());
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title:Text('Demo'),
actions:[
islongpressed?TextButton(onPressed: (){
provider.delete_transaction(selectedtransaction!);
},child: Text('Delete',style: TextStyle(color: Colors.white),),):Text('')
]),
body: showTransactions(),
floatingActionButton: FloatingActionButton(
// backgroundColor: Colors.transparent,
// elevation: 0,
onPressed: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) {
return CreateSpend();
}));
},
child: Icon(Icons.add),
),
);
}
Widget showTransactions() {
return GetBuilder<TransactionProvider>(
builder: (_) {
return provider.allTransactions.length > 0
? ListView.builder(
itemCount: provider.showRecentTransactions.length,
itemBuilder: (context, index) {
final trans = provider.showRecentTransactions[index];
return TransactionCard(
ontap: (){
islongpressed = false;
selectedindex = null;
setState(() {
});
},
onlongpressed: () {
islongpressed = true;
selectedindex = index;
setState(() {
selectedtransaction = trans;
});
},
amount: trans.amount.toStringAsFixed(2),
datetime: trans.date.toString(),
paymentby: trans.paymentmode,
category: trans.category.title,
categoryicon: trans.category.iconurl,
isexpense: trans.isexpense,
isselected: selectedindex == index ? true : false,
);
})
: Center(
child: Text('No Data Found'),);
},
);
}
}

Updating a page every time I revisit the page in BottomNavigationbar

In my app the user is able to store favorite items on a different page in the bottom navigation bar. My problem is that the page does not refresh properly. If you add a favorite it gets displayed only when restarting the app.
If the favorites page is in the same widget hierarchy of the respective bottomnavitem the function works fine.
https://pastebin.com/nZ2jrLqK
class Favorites extends StatefulWidget {
const Favorites({Key? key}) : super(key: key);
#override
_FavoritesState createState() => _FavoritesState();
}
class _FavoritesState extends State<Favorites> {
// ignore: prefer_typing_uninitialized_variables
var database;
List<Mechanism> people = <Mechanism>[];
Future initDb() async {
database = await openDatabase(
join(await getDatabasesPath(), 'person_database.db'),
onCreate: (db, version) {
return db.execute(
"CREATE TABLE person(id INTEGER PRIMARY KEY, name TEXT, height TEXT, mass TEXT, hair_color TEXT, skin_color TEXT, eye_color TEXT, birth_year TEXT, gender TEXT)",
);
},
version: 1,
);
getPeople().then((value) {
setState(() {
people = value;
});
});
}
Future<List<Mechanism>> getPeople() async {
final Database db = await database;
final List<Map<String, dynamic>> maps = await db.query('person');
return List.generate(maps.length, (i) {
return Mechanism(
id: maps[i]['id'],
name: maps[i]['name'] as String,
);
});
}
#override
void initState() {
super.initState();
initDb();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: backGround,
appBar: AppBar(
backgroundColor: appbarColor,
title: const Text("Favorites"),
),
body: ListView.builder(
itemCount: people.length,
itemBuilder: (context, index) {
var person = people[index];
return ListTile(
title: Text(
person.name,
style: const TextStyle(color: titleColor),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
MechanismDtl(mechanism: person, id: index)),
);
},
);
}),
);
}
}
Edit: page where the user can store the items
class MarkFavs extends StatefulWidget {
const MarkFavs({Key key}) : super(key: key);
#override
_MarkFavsState createState() => _MarkFavsState();
}
class _MarkFavsState extends State<MarkFavs> {
TextEditingController searchController = TextEditingController();
List<People> shownList = <People>[
People(name: 'Test', id: 1),
People(name: 'Test2', id: 2),
People(name: 'Test3', id: 3)
];
List<People> initialData = <People>[
People(name: 'Test', id: 1),
People(name: 'Test2', id: 2),
People(name: 'Test3', id: 3)
];
void queryPeople(String queryString) {
if (kDebugMode) {
print("queryString = $queryString");
}
setState(() {
shownList = initialData.where((string) {
if (string.name.toLowerCase().contains(queryString.toLowerCase())) {
return true;
} else {
return false;
}
}).toList();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: backGround,
appBar: AppBar(
backgroundColor: appbarColor,
title: const Text('Detail'),
),
body: Column(
children: <Widget>[
TextButton.icon(
label: const Text('Favorites'),
icon: const Icon(
Icons.storage,
color: titleColor,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const Favorites()),
);
},
),
Expanded(
child: PeopleList(
people: shownList,
),
),
],
),
);
}
}
class PeopleList extends StatelessWidget {
final List<People> people;
const PeopleList({Key key, this.people}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: people.length,
itemBuilder: (context, index) {
var person = people[index];
var name = person.name;
return ListTile(
title: Text(
name,
style: const TextStyle(color: titleColor),
),
onTap: () {
person.id = index;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
MechanismDtl(mechanism: person, id: index)),
);
},
);
},
);
}
}
Maybe not the most efficient method, but if you provide a passback to the Favourites class from the parent widget you can call a setState in the parent widget (assuming the parent widget reloads the database).
class Favorites extends StatefulWidget {
const Favorites({Key? key, this.passback}) : super(key: key);
final Function passback;
#override
_FavoritesState createState() => _FavoritesState();
}
Then the passback would look like:
passback() {
setState(){
//Reload db
}
}
And pass it into Favourite (does not work with named routes AFAIK)
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Favourites(passback: passback)),
);
Then just call passback when the user adds the item to their favourites.
Solved it quite ugly but it is working. If you know a better way please let me know!
class Favorites extends StatefulWidget {
const Favorites({Key? key}) : super(key: key);
#override
_FavoritesState createState() => _FavoritesState();
}
class _FavoritesState extends State<Favorites> {
// ignore: prefer_typing_uninitialized_variables
var database;
List<TestItems> items = <TestItems>[];
Future initDb() async {
database = await openDatabase(
join(await getDatabasesPath(), 'person_database.db'),
onCreate: (db, version) {
db.execute(
"CREATE TABLE person(id INTEGER PRIMARY KEY, name TEXT)",
);
},
version: 1,
);
getItems().then((value) {
setState(() {
items = value;
});
});
}
Future<List<TestItems>> getItems() async {
final Database db = await database;
final List<Map<String, dynamic>> maps = await db.query('person');
return List.generate(maps.length, (i) {
return TestItems(
id: maps[i]['id'],
name: maps[i]['name'] as String,
);
});
}
Future<void> deleteDB(int id) async {
final db = await database;
await db.delete(
'person',
where: "id = ?",
whereArgs: [id],
);
}
#override
void initState() {
super.initState();
initDb();
}
#override
Widget build(BuildContext context) {
// Updates the page every time the build method gets called
initDb().then((value) {
setState(() {
items = value;
});
});
return Scaffold(
appBar: AppBar(
title: const Text("Favorites"),
),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
var item = items[index];
return ListTile(
title: Text(
item.name,
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ItemDtl(items: item, id: index)),
);
},
trailing: IconButton(
color: Colors.red,
icon: const Icon(Icons.delete_forever_rounded),
onPressed: () {
deleteDB(item.id).then((value) {
getItems().then((value) {
setState(() {
items = value;
});
});
});
},
),
);
}));
}
}

Flutter Navigator 2.0 pages using with showGeneralDialog to handle clearing pages

I am trying to develop a logout feature with Navigator 2.0 pages & showGeneralDialog. A dialog (created by showGeneralDialog) will handle the logout after a button in the dialog is clicked by the user and the dialog is closed. However, an error is thrown in _RouteEntry.markForComplete (the error is thrown by the assert statement).
I tried to create a dummy project with some simple code:
import 'package:flutter/material.dart';
void main() {
runApp(BooksApp());
}
class Book {
final String title;
final String author;
Book(this.title, this.author);
}
class BooksApp extends StatefulWidget {
#override
State<StatefulWidget> createState() => _BooksAppState();
}
class _BooksAppState extends State<BooksApp> {
BookRouterDelegate _routerDelegate = BookRouterDelegate();
BookRouteInformationParser _routeInformationParser =
BookRouteInformationParser();
PlatformRouteInformationProvider _platformRouteInformationProvider =
PlatformRouteInformationProvider(
initialRouteInformation: RouteInformation(location: '/'));
#override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'Books App',
routerDelegate: _routerDelegate,
routeInformationParser: _routeInformationParser,
routeInformationProvider: _platformRouteInformationProvider,
);
}
}
class BookRouteInformationParser extends RouteInformationParser<RoutePath> {
#override
Future<RoutePath> parseRouteInformation(
RouteInformation routeInformation) async {
final uri = Uri.parse(routeInformation.location);
if (uri.pathSegments.length >= 2) {
var remaining = uri.pathSegments[1];
return RoutePath.details(int.tryParse(remaining));
} else if (uri.pathSegments.length > 0 && uri.pathSegments[0] == 'book') {
return RoutePath.home();
} else
return RoutePath.login();
}
#override
RouteInformation restoreRouteInformation(RoutePath path) {
if (path.isLogin) return RouteInformation(location: '/');
if (path.isHomePage) {
return RouteInformation(location: '/book');
}
if (path.isDetailsPage) {
return RouteInformation(location: '/book/${path.id}');
}
return null;
}
}
class BookRouterDelegate extends RouterDelegate<RoutePath>
with ChangeNotifier, PopNavigatorRouterDelegateMixin<RoutePath> {
final GlobalKey<NavigatorState> navigatorKey;
Book _selectedBook;
List<Book> books = [
Book('Stranger in a Strange Land', 'Robert A. Heinlein'),
Book('Foundation', 'Isaac Asimov'),
Book('Fahrenheit 451', 'Ray Bradbury'),
];
BookRouterDelegate() : navigatorKey = GlobalKey<NavigatorState>();
bool _showLogin = false;
RoutePath get currentConfiguration => _showLogin
? RoutePath.login()
: _selectedBook == null
? RoutePath.home()
: RoutePath.details(books.indexOf(_selectedBook));
#override
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
pages: [
if (currentConfiguration.isLogin)
MaterialPage(
key: ValueKey('LoginPage'),
child: Scaffold(
appBar: AppBar(
title: Text('Login'),
),
),
),
if (!currentConfiguration.isLogin)
MaterialPage(
key: ValueKey('BooksListPage'),
child: BooksListScreen(
books: books,
onTapped: _handleBookTapped,
),
),
if (_selectedBook != null) BookDetailsPage(book: _selectedBook)
],
onPopPage: (route, result) {
if (!route.didPop(result)) {
return false;
}
// Update the list of pages by setting _selectedBook to null
_selectedBook = null;
notifyListeners();
return true;
},
);
}
#override
Future<void> setNewRoutePath(RoutePath path) async {
if (path.isDetailsPage) {
_selectedBook = books[path.id];
}
}
void _handleBookTapped(Book book) {
_selectedBook = book;
notifyListeners();
}
void handleLogout() {
_showLogin = true;
_selectedBook = null;
notifyListeners();
}
}
class BookDetailsPage extends Page {
final Book book;
BookDetailsPage({
this.book,
}) : super(key: ValueKey(book));
Route createRoute(BuildContext context) {
return MaterialPageRoute(
settings: this,
builder: (BuildContext context) {
return BookDetailsScreen(book: book);
},
);
}
}
class RoutePath {
final bool isLogin;
final int id;
RoutePath.login()
: id = null,
isLogin = true;
RoutePath.home()
: id = null,
isLogin = false;
RoutePath.details(this.id) : isLogin = false;
bool get isHomePage => id == null;
bool get isDetailsPage => id != null;
}
class BooksListScreen extends StatelessWidget {
final List<Book> books;
final ValueChanged<Book> onTapped;
BooksListScreen({
#required this.books,
#required this.onTapped,
});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: ListView(
children: [
for (var book in books)
ListTile(
title: Text(book.title),
subtitle: Text(book.author),
onTap: () => onTapped(book),
)
],
),
);
}
}
class BookDetailsScreen extends StatelessWidget {
final Book book;
BookDetailsScreen({
#required this.book,
});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: Icon(Icons.exit_to_app),
onPressed: () async {
final response = await showGeneralDialog<bool>(
context: context,
useRootNavigator: true,
barrierDismissible: false,
transitionDuration: const Duration(milliseconds: 300),
transitionBuilder: (context, animation, __, child) {
return ScaleTransition(
scale: animation,
child: child,
);
},
pageBuilder: (context, _, __) => _CustomDialog(),
);
if (response == null) return;
if (response) {
// await Future.delayed(const Duration(milliseconds: 300));
context
.findAncestorStateOfType<_BooksAppState>()
._routerDelegate
.handleLogout();
}
},
),
],
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (book != null) ...[
ListTile(
title: Text(book.title),
subtitle: Text(book.author,
style: Theme.of(context).textTheme.subtitle1),
),
],
],
),
),
);
}
}
class _CustomDialog extends StatelessWidget {
_CustomDialog({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Dialog(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Clear all pages?'),
RaisedButton(
child: Text('OK'),
onPressed: () {
Navigator.of(context, rootNavigator: true).pop(true);
},
),
],
),
),
);
}
}
However, in this dummy project, the error is randomly thrown, sometimes it is thrown at NavigatorState.finalizeRoute (in this assert statement: assert(_history.where(_RouteEntry.isRoutePredicate(route)).length == 1);), and sometimes it is thrown at exactly the same as the one I mention which is _RouteEntry.markForComplete.
The workaround I can find currently is to delay until the dialog pop transition is completed (delay before context.findAncestorStateOfType<_BooksAppState>()._routerDelegate.handleLogout();).
However, I would like to know the proper fix for this instead of waiting for it to completely pop because I am unsure if there is any hidden trouble I might face.
The issue seems to be fixed on the issue ticket you've filed. No workarounds needed. Just update the Flutter SDK version to fix the issue.

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());
}
}

How to use SearchDelegate to show recent search history in flutter?

I want to use showSearch to get the search text( or query) from the user. I also want to show the recent searches as suggestions and filter search history based on the text entered.
So how do I achieve this?
custom_search_delgates.dart
import 'package:flutter/material.dart';
typedef OnSearchChanged = Future<List<String>> Function(String);
class SearchWithSuggestionDelegate extends SearchDelegate<String> {
///[onSearchChanged] gets the [query] as an argument. Then this callback
///should process [query] then return an [List<String>] as suggestions.
///Since its returns a [Future] you get suggestions from server too.
final OnSearchChanged onSearchChanged;
///This [_oldFilters] used to store the previous suggestions. While waiting
///for [onSearchChanged] to completed, [_oldFilters] are displayed.
List<String> _oldFilters = const [];
SearchWithSuggestionDelegate({String searchFieldLabel, this.onSearchChanged})
: super(searchFieldLabel: searchFieldLabel);
///
#override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context),
);
}
#override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
icon: Icon(Icons.clear),
onPressed: () => query = "",
),
];
}
///OnSubmit in the keyboard, returns the [query]
#override
void showResults(BuildContext context) {
close(context, query);
}
///Since [showResults] is overridden we can don't have to build the results.
#override
Widget buildResults(BuildContext context) => null;
#override
Widget buildSuggestions(BuildContext context) {
return FutureBuilder<List<String>>(
future: onSearchChanged != null ? onSearchChanged(query) : null,
builder: (context, snapshot) {
if (snapshot.hasData) _oldFilters = snapshot.data;
return ListView.builder(
itemCount: _oldFilters.length,
itemBuilder: (context, index) {
return ListTile(
leading: Icon(Icons.restore),
title: Text("${_oldFilters[index]}"),
onTap: () => close(context, _oldFilters[index]),
);
},
);
},
);
}
}
Usage:
import 'package:flutter/material.dart';
import 'package:flutter_app/custom_search_delgates.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(MaterialApp(home: Home()));
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
Future<void> _showSearch() async {
final searchText = await showSearch<String>(
context: context,
delegate: SearchWithSuggestionDelegate(
onSearchChanged: _getRecentSearchesLike,
),
);
//Save the searchText to SharedPref so that next time you can use them as recent searches.
await _saveToRecentSearches(searchText);
//Do something with searchText. Note: This is not a result.
}
Future<List<String>> _getRecentSearchesLike(String query) async {
final pref = await SharedPreferences.getInstance();
final allSearches = pref.getStringList("recentSearches");
return allSearches.where((search) => search.startsWith(query)).toList();
}
Future<void> _saveToRecentSearches(String searchText) async {
if (searchText == null) return; //Should not be null
final pref = await SharedPreferences.getInstance();
//Use `Set` to avoid duplication of recentSearches
Set<String> allSearches =
pref.getStringList("recentSearches")?.toSet() ?? {};
//Place it at first in the set
allSearches = {searchText, ...allSearches};
pref.setStringList("recentSearches", allSearches.toList());
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Search Demo"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
onPressed: _showSearch,
),
],
),
);
}
}
import 'package:flutter/material.dart';
class Searching extends SearchDelegate {
#override
Widget? buildLeading(BuildContext context) {
return IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => close(context, null),
);
}
#override
List<Widget>? buildActions(BuildContext context) {
return [
IconButton(
icon: const Icon(Icons.clear),
// color: Colors.grey[800],
onPressed: () {
if (query.isEmpty) {
close(context, null);
} else {
query = '';
}
},
),
];
}
#override
Widget buildResults(BuildContext context) => SearchResult(query: query);
// search history
#override
Widget buildSuggestions(BuildContext context) {
List suggestions = localUser.searches!.where((search) {
return search.toLowerCase().contains(query.toLowerCase());
}).toList();
return ListView.builder(
itemCount: suggestions.length,
itemBuilder: (context, index) {
final suggestion = suggestions[index];
return ListTile(
leading: Icon(Icons.history, size: 22),
title: Text(suggestion, style: TextStyle(fontSize: 16)),
onTap: () {
query = suggestion;
showResults(context);
},
trailing: IconButton(
icon: Icon(Icons.close),
iconSize: 15,
onPressed: () {
localUser.removeSearched(suggestion);
UserPreferences.setUser(localUser);
buildSuggestions(context);
},
),
);
},
);
}
}