Flutter: Maintain state when navigating to different page - flutter

I have the following code which creates a Listview and adds an icon to the end of each item. This icon can be tapped in order to highlight it.
The problem is, when I navigate away from the page the items are deselected. I am using Navigator.of to load the Listview:
Navigator.of(context)
.push(MaterialPageRoute<Null>(builder: (BuildContext context) {
return new LikedList();
}));
Full code:
import 'package:flutter/material.dart';
import './images.dart';
class LikedList extends StatefulWidget {
#override
_LikedListState createState() => _LikedListState();
}
class _LikedListState extends State<LikedList> {
List<bool> _likes = List.filled(ImagesState.likes.length,true);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Liked List'),
),
body: ListView.separated(
separatorBuilder: (context, index) => Divider(),
itemCount: ImagesState.likes.length,
itemBuilder: (context, index) {
final item = ImagesState.likes[index];
return ListTile(
title: Text(item),
trailing: IconButton(
icon: _likes[index]
? Icon(
Icons.favorite_border,
color: Colors.grey,
)
: Icon(
Icons.favorite,
color: Colors.red,
),
onPressed: () {
setState(() {
print(_likes);
_likes[index] = !_likes[index];
print(_likes);
});
},
),
onLongPress: () {
setState(() {
print(ImagesState.likes[index]);
ImagesState.likes.removeAt(index);
});
},
);
},
),
);
}
}

Related

Delete Specific ListTile from ListView.builder with longPress

In ListView.builder I'm adding a new ListTile with the button Pressed.
Now when I press on ListTile I want to delete that widget.
I have tried to do that by wrapping the widget with InkWell but when I try to delete it deletes from the last ListTile.
How to delete that specific ListTile when I longPressed on that.
Below here is the code
import 'package:flutter/material.dart';
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
/*InkWell(
child: widgets[index],
onLongPress: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Delete?'),
actions: [
IconButton(
onPressed: () {
widgets.removeAt(index);
setState(() {
Navigator.pop(context);
});
},
icon: Icon(Icons.check))
],
));
},
);*/
class _HomeState extends State<Home> {
#override
List<Widget> widgets = [];
int inde = 0;
List<List> blogList = [];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Note'),
centerTitle: true,
),
body: Column(children: [
Expanded(
child: ListView.builder(
itemCount: widgets.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return InkWell(
child: widgets[index],
onLongPress: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Delete?'),
actions: [
IconButton(
onPressed: () {
widgets.removeAt(index);
setState(() {
Navigator.pop(context);
});
},
icon: Icon(Icons.check))
],
));
},
);
},
),
),
FloatingActionButton(
onPressed: () {
setState(() {
widgets.add(Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: 150,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
border: Border.all(width: 2),
color: Color.fromARGB(255, 76, 178, 204),
),
child: ListTile(
leading: Icon(Icons.circle),
title: TextField(),
)),
));
});
},
child: Icon(Icons.add),
),
]));
}
}
Actually your code works, it deletes the ListTile which you use long press on.
The problem is that you do not assign different controllers to the TextField widgets. So if you enter some text into them, and call setState when deleting one, the values in the TextFields will be wrong, and it looks like the last one is deleted.
So you need to add the following logic to your code:
Create another list like widgets for the controllers.
When adding a new item, create a new controller and assign it to the TextField.
When deleting an item, dispose the controller and remove it from the controllers' list.
Don't forget to dispose all remaining controllers when the widget is disposed.
Here is a sample code, check for the comments where I added to your code. You can run it on DartPad.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: Home(),
),
),
);
}
}
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<Widget> widgets = [];
// this is the list for the controllers
List<TextEditingController> controllers = [];
int inde = 0;
List<List> blogList = [];
// you need to add this in order to dispose
// the controllers when the widget is disposed
#override
void dispose() {
for (var controller in controllers) {
controller.dispose();
}
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Note'),
centerTitle: true,
),
body: Column(children: [
Expanded(
child: ListView.builder(
itemCount: widgets.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return InkWell(
child: widgets[index],
onLongPress: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Delete?'),
actions: [
IconButton(
onPressed: () {
setState(() {
widgets.removeAt(index);
// dispose the controller
controllers[index].dispose();
// remove the controller from list
controllers.removeAt(index);
});
Navigator.pop(context);
},
icon: const Icon(Icons.check))
],
));
},
);
},
),
),
FloatingActionButton(
onPressed: () {
setState(() {
// create a new controller and add it to the list
final newController = TextEditingController();
controllers.add(newController);
widgets.add(Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: 150,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
border: Border.all(width: 2),
color: const Color.fromARGB(255, 76, 178, 204),
),
child: ListTile(
leading: const Icon(Icons.circle),
// assign the controller to the field
title: TextField(controller: newController),
)),
));
});
},
child: const Icon(Icons.add),
),
]));
}
}
I suggest that following the convention, begin all private members of your state class with an underscore, so rename controllers to _controllers etc.

type 'String' is not a subtype of type 'Description'

Hello everyone i am new in flutter. I am trying to take data from API. When i try to take, i can get the body but i cannot get the datas from into it. I get an error like "I/flutter ( 8981): type 'String' is not a subtype of type 'Description'". Here is a screenshot for from my console when i run the code: Console output
and here is my code:
import 'dart:convert';
import 'dart:ffi';
import 'package:feedme_start/main.dart';
import 'package:feedme_start/model/AnaEkran_modeli.dart';
import 'package:feedme_start/model/branch_list.dart';
import 'package:feedme_start/model/restourantList.dart';
import 'package:feedme_start/widgets/Navigation_Drawer_Widget.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
class restaurantPage extends StatefulWidget {
const restaurantPage({Key? key}) : super(key: key);
#override
_restaurantPageState createState() => _restaurantPageState();
}
final datas = [];
final datas_oneri = [];
class _restaurantPageState extends State<restaurantPage> {
int counter = 0;
var personalResult;
Future getapidata() async {
//orjinal api denemesi
String url =
"https://www.mobilonsoft.com/fpsapi/default.aspx?op=branch_list&firmuid=feedmekktc&device_id=web_20210813180900001&device_platform=4&lang=en";
try {
Response responsee = await get(Uri.parse(url)); //yanıtı alır
if (responsee.statusCode == 200) {
// yanıt onaylanırsa
Map sonuc = jsonDecode(responsee.body); //içeriği alır
print("apideki veriler ;");
print(sonuc);
**Branchlist liste = Branchlist.fromJson(sonuc);**
print(liste.result.branchList.toString());
} else {}
} catch (e) {
print(e.toString());
print("verileri çekerken hata oluştu");
}
}
#override
void initState() {
getapidata();
}
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
elevation: 0,
leading: IconButton(
onPressed: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) => MyApp()));
},
icon: Icon(Icons.arrow_back)),
backgroundColor: Colors.red,
actions: <Widget>[
IconButton(
onPressed: () {
showSearch(context: context, delegate: dataSearch());
},
icon: Icon(Icons.search)),
],
),
backgroundColor: Colors.white,
body: Center(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: ListView.builder(
itemCount: counter,
itemBuilder: (context, index) {
return ListTile(
title: Text(personalResult.data[index].firstName +
" " +
personalResult.data[index].lastName),
subtitle: Text(personalResult.data[index].email),
leading: CircleAvatar(
backgroundImage:
NetworkImage(personalResult.data[index].avatar),
),
);
}),
),
),
),
);
}
}
class dataSearch extends SearchDelegate<String> {
#override
List<Widget> buildActions(BuildContext context) {
// actions for appbar
return [
IconButton(
onPressed: () {
query = "";
},
icon: Icon(Icons.clear))
];
}
#override
Widget buildLeading(BuildContext context) {
// leading icon on the left of the app bar
return IconButton(
icon: AnimatedIcon(
icon: AnimatedIcons.menu_arrow, progress: transitionAnimation),
onPressed: () {
close(context, query);
},
);
}
#override
Widget buildResults(BuildContext context) {
// show some result based on the selection
return Container(
color: Colors.red,
child: Card(),
);
throw UnimplementedError();
}
#override
Widget buildSuggestions(BuildContext context) {
// show when someone searches for something
final datasOnerisi = query.isEmpty
? datas_oneri
: datas.where((p) => p.startsWith(query)).toList();
return ListView.builder(
itemBuilder: (context, index) => ListTile(
onTap: () {
showResults(context);
},
leading: Icon(Icons.search),
title: RichText(
text: TextSpan(
text: datasOnerisi[index].substring(0, query.length),
style:
TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
children: [
TextSpan(
text: datasOnerisi[index].substring(query.length),
style: TextStyle(color: Colors.grey))
]),
),
),
itemCount: datasOnerisi.length,
);
throw UnimplementedError();
}
}
I have solved the issue by changing my Model class... There were some mistakes about that. A 'string' was saved as'description' thats why it was giving this error. So i changed it with string and it solved. It was an little easy but sneaky mistake

Search Listview In Flutter

I have searched the web on how to add search functionality to my Flutter application. I have a list view that gets data from a mysql database and pass it as a json string. I want to add a search box on top of the list, when the user enters a string, it will take the string and search the name field of the list and re-populate the list. Here is my code please.
Thank you.
class Home extends StatefulWidget {
Home({Key key}) : super(key: key);
#override
HomeState createState() => HomeState();
}
class HomeState extends State<Home> {
Future<List<Attendants>> students;
final studentListKey = GlobalKey<HomeState>();
#override
void initState() {
super.initState();
students = getStudentList();
}
Future<List<Attendants>> getStudentList() async {
final response = await http.get("${Env.URL_PREFIX}/list.php");
final items = json.decode(response.body).cast<Map<String, dynamic>>();
List<Attendants> students = items.map<Attendants>((json) {
return Attendants.fromJson(json);
}).toList();
return students;
}
void refreshStudentList() {
setState(() {
students = getStudentList();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: studentListKey,
appBar: AppBar(
title: Text('Members List'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search,
color: Colors.white,),
onPressed: null),
IconButton(
icon: Icon(Icons.refresh),
onPressed: () {
refreshStudentList();
},
)
],
),
body: Center(
child: FutureBuilder<List<Attendants>>(
future: students,
builder: (BuildContext context, AsyncSnapshot snapshot) {
// By default, show a loading spinner.
if (!snapshot.hasData) return CircularProgressIndicator();
// Render student lists
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
var data = snapshot.data[index];
return Card(
child: ListTile(
leading: Icon(Icons.person),
trailing: Icon(Icons.view_list),
title: Text(
data.name,
style: TextStyle(fontSize: 20),
),
onTap: () {
Navigator.push(
context,
EnterExitRoute(
exitPage: Home(),
enterPage: Details(attendants: data),
),
);
},
),
);
},
);
},
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return Create();
}));
},
),
);
}
}
Let me know if my question is not clear so I can explain it further
If you get all objects about your search in the future function
add your code :
// By default, show a loading spinner.
if (!snapshot.hasData) return CircularProgressIndicator();
var list = snapshot.data.where((student){
// Or some other query..
return student[“some”].contains(searchTerm);
}).toList();
return ListView.builder(
itemCount: list.length,
itemBuilder: (BuildContext context, int index) {
var data = list[index];
..............
Else if not, this is a different subject in Flutter.

Flutter: Favourite item in listview

I am trying to add a favourite button to list view, all it needs to be is an icon on the end of each list item that you can tap to change the colour of it.
Here is what I have so far:
import 'package:flutter/material.dart';
import './images.dart';
class LikedList extends StatefulWidget {
#override
_LikedListState createState() => _LikedListState();
}
class _LikedListState extends State<LikedList> {
List<bool> _likes = [];
Color _iconColor = Colors.grey[700];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Liked List'),
),
body: ListView.separated(
separatorBuilder: (context, index) => Divider(),
itemCount: ImagesState.likes.length,
itemBuilder: (context, index) {
final item = ImagesState.likes[index];
return ListTile(
title: Text(item),
trailing: IconButton(
icon: _likes[index]
? Icon(
Icons.favorite_border,
color: Colors.grey,
)
: Icon(
Icons.favorite,
color: Colors.red,
),
onPressed: () {
setState(() {
_likes[index] = !_likes[index];
});
},
),
onLongPress: () {
setState(() {
print(ImagesState.likes[index]);
ImagesState.likes.removeAt(index);
});
},
);
},
),
);
}
}
However, this is resulting in:
RangeError (index): Invalid value: Valid value range is empty: 0
Does anyone know why this isn't working?
Your _likes list is Empty.
Initialize your _likes list with boolean values as per ypur total items of list you are showing.

How to change state of MaterialPageRoute?

I was following the tutorial from the Flutter docs where you create a Startup naming app. The app consists in two pages: one where there's an infinite list of randomly generated startup names that you can add to your favorites, and a favorites page where you can see the names you saved.
After completing the tutorial, I tried to add some functionality of my own, I wanted to be able to Unfavorite a name by tapping it on the "Favorites" page. Below is the code that pushes the Favorites page to the navigator:
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context) {
final Iterable<ListTile> tiles = _saved.map(
(WordPair pair) {
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
// Code I added //
trailing: Icon(Icons.delete),
onTap: () {
setState(() {
_saved.remove(pair);
});
},
// End //
);
},
);
final List<Widget> divided = ListTile
.divideTiles(
context: context,
tiles: tiles,
)
.toList();
return Scaffold(
appBar: AppBar(
title: Text('Saved suggestions'),
),
body: ListView(children: divided),
);
},
),
);
}
But it didn't worked as it should: you can indeed unsave names by tapping them, but the changes will only be shown on the screen after you go back to the main page and then to the favorites page again (or in other words, when Builder is called?).
So how do I fix this? Do I need to create a Stateful widget for the favorites page? If yes, how do I pass the _saved set to my new widget?
If anybody needs the whole code:
https://pastebin.com/asLneaKe
Wrap with StatefulBuilder works fine.
You can see full code and working demo
code snippet
MaterialPageRoute<void>(
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
final Iterable<ListTile> tiles = _saved.map(
working demo
full code
import 'package:english_words/english_words.dart' as prefix0;
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Startup Name Generator',
theme: ThemeData(
primaryColor: Colors.white,
),
home: RandomWords(),
);
}
}
class RandomWords extends StatefulWidget {
#override
RandomWordsState createState() => RandomWordsState();
}
class RandomWordsState extends State<RandomWords> {
final List<WordPair> _suggestions = <WordPair>[];
final Set<WordPair> _saved = Set<WordPair>();
final TextStyle _biggerFont = const TextStyle(fontSize: 18.0);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Startup Name Generator'), actions: <Widget>[
// Icone 3 linhas
IconButton(
icon: Icon(Icons.list),
onPressed: _pushSaved,
),
]),
body: _buildSuggestions(),
);
}
Widget _buildRow(WordPair pair) {
final bool alreadySaved = _saved.contains(pair);
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
trailing: Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red : null,
),
onTap: () {
setState(() {
if (alreadySaved) {
_saved.remove(pair);
} else {
_saved.add(pair);
}
});
});
}
Widget _buildSuggestions() {
return ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (context, i) {
if (i.isOdd) return Divider();
final index = i ~/ 2;
if (index >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
},
);
}
void _pushSaved() {
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
final Iterable<ListTile> tiles = _saved.map(
(WordPair pair) {
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
// Code I added //
trailing: Icon(Icons.delete),
onTap: () {
setState(() {
_saved.remove(pair);
});
},
// End //
);
},
);
final List<Widget> divided = ListTile.divideTiles(
context: context,
tiles: tiles,
).toList();
return Scaffold(
appBar: AppBar(
title: Text('Saved suggestions'),
),
body: ListView(children: divided),
);
});
},
),
);
}
}