I'm trying to implement a search bar in my app that displays songs, which the user is then able to search through to find a specific song. I pass the List songs into my CustomSearchDelegate class, but I can't access the variable songTitle from the for loop in buildResults and buildSuggestions. Relevant code below:
import 'package:flutter/material.dart';
class Recordings extends StatelessWidget {
Recordings({Key? key, required this.title}) : super(key: key);
final String title;
//This will be replaced by data from the database
final List<Widget> songs = [
const Song(songTitle: "September"),
const Song(songTitle: "Don't Stop Me Now"),
const Song(songTitle: "Let It Go"),
const Song(songTitle: "Smoke on the Water"),
const Song(songTitle: "Don't Kill My Vibe"),
const Song(songTitle: "Mamma Mia"),
const Song(songTitle: "4'33"),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
actions: [
IconButton(
onPressed: () {
showSearch(context: context, delegate: CustomSearchDelegate(searchTerms: songs));
},
icon: const Icon(Icons.search)),
],
),
body: Container(
color: Colors.grey[600],
child: Center(
child: ListView(
children: songs,
),
),
),
);
}
}
class Song extends StatelessWidget {
const Song({Key? key, required this.songTitle}) : super(key: key);
final String songTitle;
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(10),
child: Container(
alignment: Alignment.center,
color: Colors.lightBlue[300],
child: Text(
songTitle,
style: const TextStyle(color: Colors.black),
),
),
);
}
}
class CustomSearchDelegate extends SearchDelegate {
CustomSearchDelegate({required this.searchTerms});
final List<Widget> searchTerms;
#override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
onPressed: () {
query = "";
},
icon: const Icon(Icons.clear),
),
];
}
#override
Widget buildLeading(BuildContext context) {
return IconButton(
onPressed: () {
close(context, null);
},
icon: const Icon(Icons.arrow_back),
);
}
#override
Widget buildResults(BuildContext context) {
List<Widget> matchQuery = [];
//looping through each item in searchTerms
for (var song in searchTerms) {
if (song.title.toLowerCase().contains(query.toLowerCase())) { //ERROR OCCURS HERE
matchQuery.add(song);
}
}
return ListView.builder(
itemCount: matchQuery.length,
itemBuilder: ((context, index) {
var result = matchQuery[index].title;
return ListTile(
title: Text(result),
);
}),
);
}
#override
Widget buildSuggestions(BuildContext context) {
List<Widget> matchQuery = [];
//looping through each item in searchTerms
for (var song in searchTerms) {
if (song.title.toLowerCase().contains(query.toLowerCase())) { //ERROR OCCURS HERE
matchQuery.add(song);
}
}
return ListView.builder(
itemCount: matchQuery.length,
itemBuilder: ((context, index) {
var result = matchQuery[index].title;
return ListTile(
title: Text(result),
);
}),
);
}
}
Figured out how to fix this. I changed List<Widget> to List<Song> wherever I needed to access songTitle. I also added a getSongTitle to the Song class that returned songTitle.
Related
I need to generate an N amount of DropDownButtons fetched from FireBase when I click on a button for example. I have this code where I generate the N amount but the variables are the same for each one of them, the ideal would be to store each DropDownButton with an independent variable.
import 'package:flutter/material.dart';
class Formulario2Screen extends StatefulWidget {
const Formulario2Screen({Key? key}) : super(key: key);
#override
State<Formulario2Screen> createState() => _Formulario2ScreenState();
}
List<Widget> bodyElements = [];
int num = 0;
var selecCurrency, eleccion, selecCurrency2, eleccion2;
class _Formulario2ScreenState extends State<Formulario2Screen> {
bool pressed = false;
void addBodyElement() {
bodyElements.add(
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection('Sintomas').snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return const Text('Loading');
} else {
List<DropdownMenuItem> currencyItems = [];
for (int i = 0; i < snapshot.data.docs.length; i++) {
DocumentSnapshot snap = snapshot.data.docs[i];
currencyItems.add(
DropdownMenuItem(
child: Text(
snap.id,
),
value: snap.id,
),
);
eleccion = currencyItems;
}
}
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
DropdownButton<dynamic>(
items: eleccion,
onChanged: (currencyValue) {
var snackBar =
SnackBar(content: Text('Se seleccionó $currencyValue'));
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(snackBar);
setState(() {
selecCurrency = currencyValue;
});
},
value: selecCurrency,
hint: const Text('Selecciona un sintoma')),
],
);
},
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Center(child: Text('Home')),
brightness: Brightness.dark,
leading: IconButton(
icon: Icon(Icons.refresh),
onPressed: () {
setState(() {
bodyElements.clear();
num = 0;
});
},
),
),
body: ListView(
children: <Widget>[
Column(
children: bodyElements,
),
],
),
floatingActionButton: FloatingActionButton.extended(
icon: Icon(Icons.add),
label: Text('Add'),
onPressed: () {
num++;
setState(() {
addBodyElement();
});
},
),
);
}
}
Basically what I do is every time I press the Add button, the previously created widget is added.
I am using a bottom navigation bar with 3 pages. One of them (HomePage) has a search icon. Every time I pressed on it, the keyboard appears for barely one second, then disappears and the app rebuilds itself. I tried other solutions online such as putting the future values in initState() but it did not work. Note that I have also implemented a sign up/sign in system. When the user has signed up/ signed in, they will be then redirected to the page which has a bottom navigation bar. (Also, I did some research, and apparently, only android phones are "suffering" from this problem when the flutter app becomes complex. I don't know how much it is true though)
Here is the code for the Bottom Navigation Bar
class BottomNavigationPage extends StatefulWidget{
final AfreecaInvestAccount _afreecaInvestAccount;
_BottomNavigationPageState createState() => _BottomNavigationPageState();
BottomNavigationPage(this._afreecaInvestAccount);
}
class _BottomNavigationPageState extends State<BottomNavigationPage> {
static List<Widget> widgetOptions;
int selectedIndex = 0;
#override
void initState() {
widgetOptions =
[
HomePage(widget._afreecaInvestAccount),
StockSearchPage(),
AccountPage(widget._afreecaInvestAccount)
];
super.initState();
}
void onTabTapped(int index) {
setState(() {
selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: widgetOptions.elementAt(selectedIndex),
bottomNavigationBar: _bottomNavigationBar(),
);
}
BottomNavigationBar _bottomNavigationBar() {
return BottomNavigationBar(
items: const<BottomNavigationBarItem>
[
BottomNavigationBarItem(icon: Icon(Icons.home), label: "Home"),
BottomNavigationBarItem(icon: Icon(Icons.search), label: "Search Stocks"),
BottomNavigationBarItem(icon: Icon(Icons.account_box), label: "Account"),
],
currentIndex: selectedIndex,
onTap: onTabTapped,
);
}
}
Here is the code for the Home Page:
class HomePage extends StatefulWidget {
final AfreecaInvestAccount _afreecaInvestAccount;
#override
_HomePage createState() => _HomePage(_afreecaInvestAccount);
HomePage(this._afreecaInvestAccount);
}
class _HomePage extends State<HomePage> {
Future<List<int>> _futureBPandOSValue;
final AfreecaInvestAccount _afreecaInvestAccount;
FutureBuilder<List<int>> _futureBuilder;
String _accessToken;
List<String> list =
[
"TSLA",
"AMZN",
"GME",
"AAPL"
];
_HomePage(this._afreecaInvestAccount);
#override
void initState() {
// TODO: implement initState
// timer = Timer.periodic(Duration(seconds: 30), (timer) {build(context);});
_accessToken = _afreecaInvestAccount.accessToken;
_futureBPandOSValue = _futureBPAndOSValues(_accessToken);
_futureBuilder = FutureBuilder<List<int>> (
future: _futureBPandOSValue,
builder: _BPandOSValueBuilder,
);
super.initState();
}
void buildWidget() {
setState(() {
});
}
#override
Widget build(BuildContext context) {
// String _selectedItem;
return Scaffold(
appBar: AppBar(
title: Text('Home'),
automaticallyImplyLeading: false,
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () {
showSearch(context: context, delegate: StockSearch());
},
)
],
),
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_futureBuilder,
SizedBox(height: 10.0,),
Card(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: _returnStocksTable(context, _accessToken),
/* _returnStocksTable method is not included in this code block because it is not the cause of the problem */
)
),
],
),
)
);
}
}
For this code, showSearch is already provided by flutter itself :
onPressed: () {
showSearch(context: context, delegate: StockSearch());
},
Here is the StockSearch() file:
class StockSearch extends SearchDelegate<String> {
String accessToken = "djdjicjscnjdsncjs";
List<String> stockList =
[
"TSLA",
"AMZN",
"GME",
"AAPL"
];
final recentStocks = ['TSLA'];
#override
List<Widget> buildActions(BuildContext context) {
// actions for app bar
return [
IconButton(icon: Icon(Icons.clear), onPressed: () {
query = "";
})
];
}
#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, null);
}
);
}
#override
Widget buildResults(BuildContext context) {
return Container();
}
#override
Widget buildSuggestions(BuildContext context) {
// show when someone searches for stocks
final suggestionList = query.isEmpty? recentStocks
: stockList.where((stock) => stock.startsWith(query)).toList();
return ListView.builder(
itemBuilder: (context, index) => ListTile(
onTap: () {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
Navigator.push(context, MaterialPageRoute(builder: (context) => TransactionPage(suggestionList.elementAt(index), accessToken)));
});
},
title: RichText(text: TextSpan(
text: suggestionList.elementAt(index).substring(0, query.length),
style: TextStyle( color: Colors.black, fontWeight: FontWeight.bold),
children: [TextSpan(
text: suggestionList.elementAt(index).substring(query.length),
style: TextStyle(color: Colors.grey)
)
]
)
),
),
itemCount: suggestionList.length,
);
}
}
Thank you in advance for your help!
I am trying to build a Reviews section for my app. In that, I'm trying to have a read more feature which obviously as the name suggests will expand the widget to show more text, upon tapping the widget.
I'm using a indexed stack with BottomNavigationBar to switch between reviews and the product page.
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'Product_Page_Widgets.dart';
void main() => runApp(Product());
class Product extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: ProductState(),
);
}
}
class ProductState extends StatefulWidget {
Map<dynamic,dynamic> productData;
CollectionReference reviews;
ProductState({#required this.productData,#required this.reviews});
#override
_ProductStateState createState() => _ProductStateState();
}
class _ProductStateState extends State<ProductState> with TickerProviderStateMixin{
List<NetworkImage> _Images = [];
List<Widget> productImages = [];
List<Widget> reviewWidgets = [];
String changeableText = "";
List<String> firstHalf = [];
List<String> secondHalf = [];
var cardHeight = [];
bool load = false;
int pageIndex = 0;
List<Widget> pageWidget = [];
addPageWidgets() async{
pageWidget.add( ProductPageWidget(
productName: widget.productData['Name'],
galleryItems: _Images,cachedImages: productImages,description:widget.productData['description'] ?? "placeholder")
);
Widget w = await buildReviewsWidgets();
pageWidget.add(w);
}
GlobalKey key = new GlobalKey();
Future <Widget> buildReviewsWidgets() async{
var docs = await widget.reviews.getDocuments();
var documents = docs.documents;
print(documents.toString());
if(docs != null) {
for (int i = 0; i < documents.length; i++) {
cardHeight.add(MediaQuery.of(context).size.height/4);
print(documents[i].data['Review']);
if(documents[i].data['Review'].toString().length>50){
firstHalf.add( documents[i].data['Review'].toString().substring(0,50));
secondHalf.add(documents[i].data['Review'].toString().substring(51));
}else{
firstHalf.add( documents[i].data['Review'].toString());
secondHalf.add("");
}
}
}
for(var doc in documents){
print(doc.data);
}
return Container(
height:MediaQuery.of(context).size.height,
child: ListView.builder(
key: key,
itemCount: documents.length,
itemBuilder: (context,index) => AnimatedSize(
vsync: this,
curve: Curves.easeIn,
duration: new Duration(milliseconds: 300),
child: Container(
height: cardHeight[index],
child: Card(
elevation: 2.0,
child: ListTile(
onTap: (){
stateUpdate(index);
},
leading: CircleAvatar(),
title: Text(documents[index].data['Title']),
subtitle: Column(children: [
RatingBar(
itemBuilder: (context, _) => Icon(
Icons.star,
color: Colors.amber,
),
maxRating: 5.0,
allowHalfRating: false,
ignoreGestures: true,
initialRating: int.parse(documents[index].data['Rating'].toString()).toDouble(),
),
Wrap(
children: <Widget>[
Text(firstHalf[index].toString()),
Text("Read More")
],
),
]),
),
),
),
),
),
);
}
void AddNetworkImages (BuildContext context){
if(!load) {
for (int i = 0; i < widget.productData.length; i++) {
if (widget.productData.containsKey('Img$i')) {
print("Img$i exists");
_Images.add(
NetworkImage(widget.productData['Img$i'])
);
productImages.add(
CachedNetworkImage(
imageUrl: widget.productData['Img$i'],
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
),
);
}
}
}
}
void loadWidgets(BuildContext context){
setState(() {
});
load = true;
}
#override
void initState() {
//AddNetworkImages(context);
addPageWidgets();
super.initState();
}
#override
Widget build(BuildContext context) {
WidgetsBinding.instance
.addPostFrameCallback((_) => AddNetworkImages(context));
WidgetsBinding.instance
.addPostFrameCallback((_) => loadWidgets(context));
return Scaffold(
appBar: AppBar(
actions: <Widget>[
],
title: Text("$changeableText"),
backgroundColor: Colors.deepOrangeAccent,
),
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
physics: BouncingScrollPhysics(
),
child:IndexedStack(
index: pageIndex,
children: pageWidget,
)),
bottomNavigationBar:BottomNavigationBar(
currentIndex: pageIndex,
onTap: (index){
setState(() {
pageIndex = index;
});
},
items: [
BottomNavigationBarItem(
icon: Icon(Icons.shopping_cart),
title: Text("Overview")
),
BottomNavigationBarItem(
icon: Icon(Icons.star),
title: Text("Reviews")
)
],
)
);
}
void stateUpdate(int index) {
setState(() {
firstHalf[index]+=secondHalf[index];
cardHeight[index] = MediaQuery.of(context).size.height/2;
});
print(index);
print(firstHalf[index]);
}
}
Now, inside the buildReviewsWidgets() method, i'm getting all of the review data ,iterating over it and splitting it into the firstHalf and secondHalf lists, so that, on tapping the ListTile, i can simply join firstHalf[index] and secondHalf[index], as i have done so on the ontap method of the ListTiles, but tapping on the list tile does nothing at all..the list tiles don't update, basically nothing happens. But whats interesting is that if I setState and change the value of changeableText, it does update the text on the appbar.. can anyone tell me why this is happening? how do i fix it?
I just cleaned the code up and removed the need of storing widgets with lists..not involving any lists with the widgets instantly fixed the problem.
I'm currently working on a Flutter mobile app which is supposed to work on Android and IOS.
The issue I'm having is about ListView and updating it.
I know I'm doing wrong with a lot of things but I'm learning and I would like to learn properly. So if you have any comments, tips about the code pls give them :)
Basically here is what it does :
The main player will choose who will play with him at the game via an AlertDialog which has a CheckboxList inside it and every time he selects a player, it will update a list called choosenPlayers which has all Player objects choosen in it.
Then what I want to do is to display a list of all selected players (a reorderable list to change the order of players) and update it everytime the choosenPlayers list is updated.
I managed to display these players but I have to reload the page by going in the drawer menu and clicking on page link to see added players.
I use a stateful widget for my players reorderable list and I pass to the parent the list of players (This is not the rigth way to do it I know) :
import 'package:flutter/material.dart';
import 'package:mollky/models/player.dart';
class ChoosenPlayers extends StatefulWidget {
_ChoosenPlayersState _choosenPlayersState = _ChoosenPlayersState();
List<Player> choosenPlayers = [];
ChoosenPlayers({Key key, this.choosenPlayers}) : super(key: key);
#override
_ChoosenPlayersState createState() => _choosenPlayersState;
}
class _ChoosenPlayersState extends State<ChoosenPlayers> {
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return ReorderableListView(
onReorder: onReorder,
children: getListItems(),
);
}
List<ListTile> getListItems() => widget.choosenPlayers
.asMap()
.map((i, item) => MapEntry(i, buildTenableListTile(item, i)))
.values
.toList();
ListTile buildTenableListTile(Player item, int index) {
return ListTile(
key: ValueKey(item.id),
title: Text(item.nickname + " " + item.name),
leading: Text("#${index + 1}"),
);
}
void onReorder(int oldIndex, int newIndex) {
if (newIndex > oldIndex) {
newIndex -= 1;
}
setState(() {
Player reOrderedPlayer = widget.choosenPlayers[oldIndex];
widget.choosenPlayers.removeAt(oldIndex);
widget.choosenPlayers.insert(newIndex, reOrderedPlayer);
});
}
}
Here is the code of the main page where reorderable list is displayed and AlertDialog showed.
Sorry, couldn't format with Dart, don't run the code snipped obviously xD
class NewGame extends StatefulWidget {
#override
State<StatefulWidget> createState() => NewGameState();
}
class NewGameState extends State<NewGame> {
List<Player> players = [];
NewGameState() {
this.players.add(
Player(id: 0, name: "Dupont", nickname: "julien", picture: "test"));
this
.players
.add(Player(id: 1, name: "Dpont", nickname: "julien", picture: "test"));
this
.players
.add(Player(id: 2, name: "Dunt", nickname: "juen", picture: "test"));
}
static List<Player> _choosenPlayers = [];
ChoosenPlayers choosenPlayersObject = ChoosenPlayers(
choosenPlayers: _choosenPlayers,
);
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: DrawerWidget(),
appBar: AppBar(title: Text("Nouvelle partie")),
body: Column(children: <Widget>[
Card(
child: ListTile(
leading: Icon(Icons.people),
title: Text("Choisissez les joueurs"),
onTap: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Les joueurs existants"),
content:
Stack(overflow: Overflow.visible, children: <
Widget>[
Positioned(
right: -40.0,
top: -40.0,
child: InkResponse(
onTap: () {
Navigator.of(context).pop();
},
child: CircleAvatar(
child: Icon(Icons.close),
backgroundColor: Colors.lightBlue,
),
),
),
Positioned(
child: StatefulBuilder(
builder: (BuildContext context,
StateSetter setState) {
return Container(
width: 350.0,
height: 150.0,
child: ListView.builder(
itemCount: players.length,
itemBuilder:
(context, playerIndex) {
return CheckboxListTile(
title: Text(players[playerIndex]
.nickname +
" " +
players[playerIndex].name),
value: _choosenPlayers.contains(
players[playerIndex]),
onChanged: (bool value) {
if (!_choosenPlayers.contains(
players[playerIndex])) {
_choosenPlayers.add(
players[playerIndex]);
setState(() {});
} else {
_choosenPlayers.remove(
players[playerIndex]);
setState(() {});
}
},
secondary: const Icon(
Icons.hourglass_empty),
);
}),
);
},
),
),
]));
});
})),
Container(
width: 350.0,
height: 150.0,
child: choosenPlayersObject,
),
]));
}
}
I've seen nothing on forums about updating list without triggering a callback like onRefresh which is not what I want.
It is a real nightmare xD. Sorry for french words btw I can translate if needed but they are not important, simple text.
Here are two screenshots of the list and alert dialog :
Thank you in advance :)
The state of parent widget is not updated. That's why, even though the payer is added to the list. But not shown to in parent widget.
The setState you called only update the state of StatefulBuilder not of the NewGame.
Check out the below code.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: NewGameScreen(),
);
}
}
class NewGameScreen extends StatefulWidget {
#override
_NewGameScreenState createState() => _NewGameScreenState();
}
class _NewGameScreenState extends State<NewGameScreen> {
List<Player> _availablePlayers = [];
List<Player> _selectedPlayers = [];
#override
void initState() {
super.initState();
_availablePlayers = [
Player(id: 0, name: "Ross", nickname: "Geller", picture: "test"),
Player(id: 1, name: "Rachel", nickname: "Green", picture: "test"),
Player(id: 2, name: "Chandler", nickname: "Bing", picture: "test"),
];
}
_selectPlayer() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Existing players"),
content: Stack(
overflow: Overflow.visible,
children: <Widget>[
Positioned(
right: -40.0,
top: -40.0,
child: InkResponse(
onTap: () {
Navigator.of(context).pop();
},
child: CircleAvatar(
child: Icon(Icons.close),
backgroundColor: Colors.lightBlue,
),
),
),
StatefulBuilder(
builder: (BuildContext context, StateSetter alertState) {
return Container(
width: 350.0,
height: 150.0,
child: ListView.builder(
itemCount: _availablePlayers.length,
itemBuilder: (context, playerIndex) {
return CheckboxListTile(
title:
Text(_availablePlayers[playerIndex].nickname + " " + _availablePlayers[playerIndex].name),
value: _selectedPlayers.contains(_availablePlayers[playerIndex]),
onChanged: (bool value) {
if (_selectedPlayers.contains(_availablePlayers[playerIndex])) {
_selectedPlayers.remove(_availablePlayers[playerIndex]);
} else {
_selectedPlayers.add(_availablePlayers[playerIndex]);
}
setState(() {});//ALSO UPDATE THE PARENT STATE
alertState(() {});
},
secondary: const Icon(Icons.hourglass_empty),
);
},
),
);
},
),
],
),
);
},
);
}
_onReorder(int oldIndex, int newIndex) {
if (newIndex > oldIndex) {
newIndex -= 1;
}
print('oldIndex:$oldIndex');
print('newIndex:$newIndex');
setState(() {
Player player = _selectedPlayers[newIndex];
_selectedPlayers[newIndex] = _selectedPlayers[oldIndex];
_selectedPlayers[oldIndex] = player;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("New Game")),
body: Column(
children: <Widget>[
Card(
child: ListTile(
leading: Icon(Icons.people),
title: Text("Choose players"),
onTap: _selectPlayer,
),
),
Flexible(
child: ReorderableListView(
onReorder: _onReorder,
children: _selectedPlayers.map((player) {
return ListTile(
key: ValueKey(player.id),
title: Text(player.nickname + " " + player.name),
leading: Text("#${_selectedPlayers.indexOf(player) + 1}"),
);
}).toList(),
),
),
],
),
);
}
}
class Player {
int id;
String name;
String nickname;
String picture;
Player({this.id, this.name, this.nickname, this.picture});
}
Hope it helps :)
I am trying to call a setState when a button is pressed so the ui can show the new list but even using functions i cant use setState or it will give me the error saying im calling setState inside a constructor.
This is my code for the statlessWidget:
class _MessageCard extends StatelessWidget {
final Mensagem message;
final int messageLenght;
final List<Mensagem> messageList;
var i;
_MessageCard(
{#required this.message,
#required this.messageLenght,
#required this.messageList});
#override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 600,
child: InkWell(
child: Container(
width: 900,
color: Colors.grey[200],
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 12, 0),
child: Center(
child: Container(
width: 600,
child: Column(
children: <Widget>[
ListTile(
leading: CircleAvatar(
child: Icon(
Icons.notifications,
color: Colors.red[400],
),
backgroundColor: Colors.grey[200],
),
title: Text(
(this.message.vDescricao ?? '').trim(),
style: TextStyle(
fontSize: 14,
color: Colors.black,
),
),
subtitle: Text(
(this.message.vData ?? '').trim() +
' ' +
(this.message.vHora ?? '').trim(),
style: TextStyle(
color: Colors.red[400],
fontSize: 13,
),
),
trailing: FlatButton(
child: Text(
Translations.of(context)
.trans('finishmessageshort'),
),
onPressed: () => _showDeleteAlertMessage(
this.message.vNumero, context)),
),
Divider(
color: Colors.black54,
),
],
),
),
),
),
),
),
));
}
Future _showDeleteAlertMessage(String id, BuildContext context) {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text(
Translations.of(context).trans('finishmessage') + '?',
),
actions: <Widget>[
FlatButton(
child: new Text(
Translations.of(context).trans('closealert'),
),
onPressed: () {
Navigator.of(context).pop();
}),
FlatButton(
child: new Text(("Ok")),
onPressed: () =>
{_deleteMessage(id), Navigator.of(context).pop()},
)
],
);
});
}
_deleteMessage(String id) async {
for (i = 0; i < this.messageLenght; i++) {
if (this.messageList[0].vNumero == this.message.vNumero) {
this.messageList.removeAt(i);
_HomePageState().mensagemRepo.confirmMessage(this.message.vNumero);
await _HomePageState()._getMessages();
return this.messageList;
}
}
}
}
And this is my _getMessages()
_getMessages() async {
setState(() {
_loading = true;
_errorMsg = '';
});
try {
_messages = await mensagemRepo.getMessages();
print('loaded messages: ${_messages?.length}');
} catch (e) {
_errorMsg = e.toString();
}
setState(() {
_loading = false;
});
}
How can i make it so i can use this setState?
Thank you for your time and attention
Edit: Now updates List but not UI, because im not able to set HomePage state from MessageCard
You can only use setState in a StatefulWidget.
class MessageCard extends StatefulWidget {
#override
_MessageCardState createState() => _MessageCardState();
}
class _MessageCardState extends State<MessageCard> {
#override
Widget build(BuildContext context) {
// your build method here
}
}
Well, you can't set value for something that doesn't exist. Stateless by name itself makes it clear that it can't hold any state. Changing the widget to a stateful widget would work.
Stateless widget can not change the state once its rendered. To use setState and re-render the widget StatefulWidget is used.
Just change your MessageCard from Stateless Widget to StatefulWidget
class MessageCard extends StatefulWidget {
final Mensagem message;
final int messageLenght;
final List<Mensagem> messageList;
var i;
MessageCard(
{#required this.message,
#required this.messageLenght,
#required this.messageList});
#override
_MessageCardState createState() => _MessageCardState();
}
class _MessageCardState extends State<MessageCard> {
#override
Widget build(BuildContext context) {
// your build method here
}
}
Also, now "to use your MessageCard properties" like message, messageLenght, messageList, in _MessageCardState you have to use a property like widget.message, widget.messageList and widget.messageLenght respectively.
can this work to refresh the ui?
_getMessages() async {
_HomePageState()._messages = await mensagemRepo.getMessages();
print('loaded messages: ${_messages?.length}');
setState(() {
_HomePageState()._messagesList();
});
}
The code for _messagesList() is:
SliverChildBuilderDelegate _messagesList() {
int count() {
if (_errorMsg != '')
return 1;
else
return _messages == null ? 0 : _messages.length;
}
return SliverChildBuilderDelegate(
(BuildContext context, int index) {
print("i: $index");
if (_errorMsg != '') {
return Padding(
padding: EdgeInsets.all(20),
child: ErrorMessage(
error: _errorMsg,
),
);
} else {
return _MessageCard(
message: this._messages[index],
messageLength: this._messages.length,
messageList: this._messages);
}
},
childCount: count(),
);
}
I managed to make it work, by making both classes into one and calling a function to draw the messagecards, thank you all for your help and attention