Related
I wanna pass an object list into a CupertinoPicker but it only receive a String list how can i pass it?
how to receive this data Object and correctly treat to turn every child object into a object String, then i need to add every object name into a list of strings
import 'package:flutter/cupertino.dart';
class MainCupertinoPicker extends StatefulWidget{
#override
State<StatefulWidget> createState() {
return _StateCupertinoPicker();
}
}
class _StateCupertinoPicker extends State<MainCupertinoPicker>{
int _selectedFruit = 0;
// This shows a CupertinoModalPopup with a reasonable fixed height which hosts CupertinoPicker.
void _showDialog(Widget child, {required Text}) {
showCupertinoModalPopup<void>(
context: context,
builder: (BuildContext context) => Container(
height: 216,
padding: const EdgeInsets.only(top: 6.0),
// The Bottom margin is provided to align the popup above the system navigation bar.
margin: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
// Provide a background color for the popup.
color: CupertinoColors.systemBackground.resolveFrom(context),
// Use a SafeArea widget to avoid system overlaps.
child: SafeArea(
top: false,
child: child,
),
)
);
}
double _kItemExtent = 32.0;
List<String> _fruitNames = <String>[
'Crato',
'Juazeiro',
'Fortaleza'
];
#override
Widget build(BuildContext context) {
return Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Selecione o municipio: ',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold
),
),
CupertinoButton(
padding: EdgeInsets.zero,
// Display a CupertinoPicker with list of fruits.
onPressed: () => _showDialog(
CupertinoPicker(
magnification: 1.22,
squeeze: 1.2,
useMagnifier: true,
itemExtent: _kItemExtent,
// This is called when selected item is changed.
onSelectedItemChanged: (int selectedItem) {
setState(() {
_selectedFruit = selectedItem;
print(_selectedFruit);
});
},
children:
List<Widget>.generate(_fruitNames.length, (int index) {
return Center(
child: Text(
_fruitNames[index],
),
);
}),
), Text: null,
),
// This displays the selected fruit name.
child: Text(
_fruitNames[_selectedFruit],
style: const TextStyle(
fontSize: 22.0,
),
),
),
],
),
);
}
}
I need to get the object string name add into _fruitNames list, then show to user the list, and when the user choose the name i need to return the complete object
This is the object that i wanna receive
class Tenant {
int id;
String name;
String siafi;
String url;
String username;
String password;
String driverClassName;
bool initialize;
Tenant({required this.id, required this.name, required this.siafi, required this.url, required this.driverClassName, required this.username, required this.initialize, required this.password});
factory Tenant.fromJson(Map<String, dynamic> json){
return Tenant(
id: json["id"],
name: json["name"],
siafi: json["siafi"],
url: json["url"],
driverClassName: json["driverClassName"],
username: json["username"],
initialize: json["initialize"],
password: json["password"]
);
}
}```
You don't need to create another list just for showing name of that object in CupertinoPicker you can use the same list and access the selected object with the help of _selectedfruit
You can achieve your expected behaviour with the help of code below:-
class MainCupertinoPicker extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _StateCupertinoPicker();
}
}
class _StateCupertinoPicker extends State<MainCupertinoPicker> {
int _selectedFruit = 0;
// This shows a CupertinoModalPopup with a reasonable fixed height which hosts CupertinoPicker.
void _showDialog(Widget child, {required Text}) {
showCupertinoModalPopup<void>(
context: context,
builder: (BuildContext context) => Container(
height: 216,
padding: const EdgeInsets.only(top: 6.0),
// The Bottom margin is provided to align the popup above the system navigation bar.
margin: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
// Provide a background color for the popup.
color: CupertinoColors.systemBackground.resolveFrom(context),
// Use a SafeArea widget to avoid system overlaps.
child: SafeArea(
top: false,
child: child,
),
),
);
}
final double _kItemExtent = 32.0;
/// this is the the list of your your tenant object which may be static or may be coming from api as per your usecase
List<Tenant> _tenants = [
Tenant(
id: 1,
name: "Name 1",
siafi: "Xyz",
url: "Xyz",
driverClassName: "Xyz",
username: "Xyz",
initialize: true,
password: "Xyz",
),
Tenant(
id: 1,
name: "Name 2",
siafi: "Xyz",
url: "Xyz",
driverClassName: "Xyz",
username: "Xyz",
initialize: true,
password: "Xyz",
),
Tenant(
id: 1,
name: "Name 3",
siafi: "Xyz",
url: "Xyz",
driverClassName: "Xyz",
username: "Xyz",
initialize: true,
password: "Xyz",
),
Tenant(
id: 1,
name: "Name 4",
siafi: "Xyz",
url: "Xyz",
driverClassName: "Xyz",
username: "Xyz",
initialize: true,
password: "Xyz",
),
Tenant(
id: 1,
name: "Name 5",
siafi: "Xyz",
url: "Xyz",
driverClassName: "Xyz",
username: "Xyz",
initialize: true,
password: "Xyz",
)
];
/// no need to make another list for just showing modal popup
// final List<String> _fruitNames = <String>['Crato', 'Juazeiro', 'Fortaleza'];
#override
Widget build(BuildContext context) {
return Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Selecione o municipio: ',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
CupertinoButton(
padding: EdgeInsets.zero,
// Display a CupertinoPicker with list of fruits.
onPressed: () => _showDialog(
CupertinoPicker(
magnification: 1.22,
squeeze: 1.2,
useMagnifier: true,
itemExtent: _kItemExtent,
// This is called when selected item is changed.
onSelectedItemChanged: (int selectedItem) {
setState(
() {
_selectedFruit = selectedItem;
if (kDebugMode) {
print(_tenants[_selectedFruit]);
print(_tenants[_selectedFruit].name);
}
},
);
},
children: List<Widget>.generate(
_tenants.length,
(int index) {
return Center(
child: Text(
_tenants[index].name,
),
);
},
),
),
Text: null,
),
// This displays the selected fruit name.
child: Text(
_tenants[_selectedFruit].name,
style: const TextStyle(
fontSize: 22.0,
),
),
),
],
),
);
}
}
class Tenant {
int id;
String name;
String siafi;
String url;
String username;
String password;
String driverClassName;
bool initialize;
Tenant(
{required this.id,
required this.name,
required this.siafi,
required this.url,
required this.driverClassName,
required this.username,
required this.initialize,
required this.password});
factory Tenant.fromJson(Map<String, dynamic> json) {
return Tenant(
id: json["id"],
name: json["name"],
siafi: json["siafi"],
url: json["url"],
driverClassName: json["driverClassName"],
username: json["username"],
initialize: json["initialize"],
password: json["password"]);
}
#override
String toString() {
return "id: $id, name: $name, siafi: $siafi, url: $url, driverClassName: $driverClassName, username: $username, initialize: $initialize, password: $password";
}
}
Here is the modified code,
I have created list of objects, For simplification I create own object called Person
List<Person> personList = <Person>[Person(id:1, name:'Alan'), Person(id:2, name:'Ben'), Person(id:3, name:'Cat')];
// whereas
class Person{
String name;
int id;
Person({required this.id, required this.name, });
factory Person.fromJson(Map<String, dynamic> json){
return Person(
id: json["id"],
name: json["name"],
);
}
}
You can access the person name using personList[index].name.
So that your code becomes
CupertinoButton(
padding: EdgeInsets.zero,
// Display a CupertinoPicker with list of fruits.
onPressed: () => _showDialog(
CupertinoPicker(
magnification: 1.22,
squeeze: 1.2,
useMagnifier: true,
itemExtent: kItemExtent,
// This is called when selected item is changed.
onSelectedItemChanged: (int selectedItem) {
},
children:
List<Widget>.generate(personList.length, (int index) {
return Center(
child: Text(
personList[index].name,
),
);
}),
),
context,
Text: null,
),
// This displays the selected fruit name.
child: Text(
personList[_selectedFruit].name,
style: const TextStyle(
fontSize: 22.0,
),
),
),
You have a complete object in the list, you can select the object based on index value and return the entire object like this personList[selectedIndex]
I'm facing some issues quite the time lately. I'm using the scoped_model package for my application. But i'm getting the problem that when i'm searching for stuff or i turn my screen to landscape modus it builds twice.
When this happens i'm getting duplicate json because it is rebuilding twice.
I'm getting another issue when i have searched and i go back my custom scroll view is not working anymore. I can't scroll down anymore.
I have writtin the following files:
main.dart:
class GetRelations extends StatefulWidget {
#override
RelationsPage createState() => RelationsPage();
}
class RelationsPage extends State<GetRelations> {
final RelationScopedModel relationScopedModel = RelationScopedModel();
#override
Widget build(BuildContext context) {
relationScopedModel.initializeValues();
return Scaffold(
body: ScopedModel<RelationScopedModel>(
model: relationScopedModel,
child: Container(
child: SearchScreen(),
),
),
);
}
}
search_screen.dart:
class SearchScreen extends StatefulWidget {
#override
SearchScreenState createState() {
return new SearchScreenState();
}
}
class SearchScreenState extends State<SearchScreen> {
final RelationScopedModel relationScopedModel = RelationScopedModel();
ScrollController controller;
int page = 0;
bool atEnd = false;
#override
void initState() {
super.initState();
controller = new ScrollController()..addListener(_scrollListener);
}
#override
void dispose() {
super.dispose();
controller.dispose();
}
void _scrollListener() {
var props = RelationScopedModel.of(context);
/// When reload we check if skip == 0, if skip is 0 then page has to become 0 too.
if (props.skip == 0) {
page = 0;
}
/// Checking if user is at the end of the screen, then let's receive some new content.
if (controller.position.pixels == controller.position.maxScrollExtent) {
/// If it is at the end, we have to set atEnd to true.
if (props.atTheEnd == true) {
atEnd = props.atTheEnd;
return;
}
/// If it has no more pages, return.
if (props.hasMorePages == false) {
return;
}
/// If it is has more stuff, load it in!
if (!props.isLoadingMore && props.hasMorePages) {
page++;
props.getRelations(props.search, page);
}
}
}
#override
Widget build(BuildContext context) {
/// Go back to last page by using WillPopScope.
return WillPopScope(
onWillPop: () {
Navigator.pop(context);
},
child: new Scaffold(
drawer: new DrawerOnly(),
/// We are using Scoped Model to load the json in the sliver list.
body: ScopedModelDescendant<RelationScopedModel>(
builder: (context, child, model) {
return CustomScrollView(
controller: controller,
slivers: <Widget>[
SliverAppBar(
title: SearchWidget(
performSearch: model.getRelations,
),
floating: true,
pinned: true,
),
model.isLoading && model.atTheEnd
? SliverFillRemaining(
child: Center(
child: CircularProgressIndicator(),
),
)
: model.getRelationCount() < 1
? SliverFillRemaining(
child: Center(
child: Text(
model.statusText,
style: Theme.of(context).textTheme.headline,
),
),
)
: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index == model.getRelationCount() + 1) {
if (model.hasMorePages == true) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 16.0),
child: Center(
child: CircularProgressIndicator()),
);
}
return Container(width: 0, height: 0);
} else if (index == 0) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.grey[300]))),
child: Text(
"Relaties",
style: Theme.of(context)
.textTheme
.body2
.copyWith(color: Colors.white),
),
);
} else {
return Container(
child: Column(
children: <Widget>[
InkWell(
child: RelationItem(
model.relation[index - 1]),
onTap: () {
var id =
model.relation[index - 1].id;
Navigator.of(context).push(
new MaterialPageRoute(
builder: (BuildContext
context) =>
new DetailScreen(id)));
},
),
],
),
);
}
},
childCount: model.getRelationCount() + 2,
),
)
],
);
},
),
),
);
}
}
search.dart (this is my searchwidget):
class SearchWidget extends StatelessWidget { final performSearch;
const SearchWidget({Key key, #required this.performSearch}) : super(key: key);
#override Widget build(BuildContext context) {
print('wat gebeurt er');
return Card(
elevation: 3.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(2.0)),
),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
GestureDetector(
child: Icon(
Icons.search,
color: Colors.white,
),
onTap: () {},
),
SizedBox(
width: 10.0,
),
Expanded(
child: TextField(
decoration: InputDecoration(
hintStyle: TextStyle(fontSize: 14.0, color: Colors.white),
border: InputBorder.none,
hintText: "Zoeken..."),
onChanged: (String search) {
if (search.length > 2) {
performSearch(search);
}
if (search.length == 0) {
performSearch(search);
}
},
),
),
InkWell(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => SettingsScreen()));
},
child: Icon(
FontAwesomeIcons.slidersH,
color: Colors.white,
),
),
],
),
),
); } }
relation_scoped_model.dart:
class RelationScopedModel extends Model {
List<GetRelation> _relation = [];
List<GetContacts> _contacts = [];
List<GetLocations> _locations = [];
bool _isLoading = false;
String _statusText = "Start met zoeken...";
bool _hasMorePages = true;
int _skip = 0;
int skipMore = 0;
String _search;
bool _isLoadingMore = false;
bool atTheEnd = false;
List<Map<String, String>> _listingTypeList = [
{"name": "Buy", "value": "buy"},
{"name": "Rent", "value": "rent"},
];
String _listingType;
List<Map<String, String>> _sortList = [
{"name": "Relevancy", "value": "relevancy"},
{"name": "Bedroom (Ascending)", "value": "bedroom_lowhigh"},
{"name": "Bedroom (Descending)", "value": "bedroom_highlow"},
{"name": "Price (Ascending)", "value": "price_lowhigh"},
{"name": "Price (Descending)", "value": "price_highlow"},
{"name": "Newest", "value": "newest"},
{"name": "Oldest", "value": "oldest"},
{"name": "Random", "value": "random"},
{"name": "Distance", "value": "distance"}
];
String _sort;
List<GetRelation> get relation => _relation;
List<GetContacts> get contacts => _contacts;
List<GetLocations> get locations => _locations;
bool get isLoading => _isLoading;
String get statusText => _statusText;
bool get hasMorePages => _hasMorePages;
String get search => _search;
int get skip => _skip;
bool get isLoadingMore => _isLoadingMore;
List<Map<String, String>> get listingTypeList => _listingTypeList;
String get listingType => _listingType;
List<Map<String, String>> get sortList => _sortList;
String get sort => _sort;
int getRelationCount() => _relation.length;
void initializeValues() async {
_relation.clear();
SharedPreferences prefs = await SharedPreferences.getInstance();
_listingType = prefs.getString('listingType') ?? 'rent';
_sort = prefs.getString('sort') ?? 'relevancy';
getRelations(search);
}
var _skiptotal = 10;
var lastSearch;
Future<dynamic> _getData(String search, [int page = 0]) async {
/// When page is 0 we don't have to skip content, if page is 1 or higher it will have to skip content
if (page != 0) {
var skipPage = page * _skiptotal;
_skip = skipPage;
}
/// If page == 0, we have to set page and _skip to 0.
else {
_skip = 0;
page = 0;
}
/// When nothing is filled in the input search, we have to set search to single quotes because otherwise it will search on null.
if (search == null) {
search = '';
}
if (lastSearch != search) {
_relation.clear();
lastSearch = '';
lastSearch = search;
page + 1;
}
String _credentials;
SharedPreferences pref = await SharedPreferences.getInstance();
_credentials = (pref.getString("credentials") ?? "Empty");
var res = await http.get(
Uri.encodeFull("$cf_api_RelationsUrl" +
"?$cf_api_SkipParameter=$_skip&$cf_api_SearchParameter=$search&$cf_api_LimitParameter=10"),
headers: {
"content-type": "application/json",
"accept": "application/json",
'cookie': '$_credentials'
});
var decodedJson = json.decode(res.body, reviver: (k, v) {
if (k == "status" && v == false) {
_hasMorePages = false;
return v;
} else {
return v;
}
});
if (_hasMorePages == false) {
return decodedJson;
} else {
List list = List();
list = json.decode(res.body) as List;
if (list.length < 10) {
atTheEnd = true;
_hasMorePages = false;
return decodedJson;
}
}
return decodedJson;
}
Future getRelations(String search, [int page = 0]) async {
if (page == 0) {
page = 0;
_isLoading = true;
_relation.clear();
} else {
_isLoadingMore = true;
}
_search = search;
var responseData = await _getData(search, page);
notifyListeners();
var result = responseData
.map(
(data) => serializers.deserializeWith(GetRelation.serializer, data))
.toList();
result.forEach((relations) {
_relation.add(relations);
});
if (result.isEmpty) {
_statusText = "Nothing Found";
}
if (page == 0) {
_isLoading = false;
} else {
_isLoadingMore = false;
}
if (atTheEnd == true) {
return true;
}
// notifyListeners();
}
void setListingType(String value) async {
_listingType = value;
getRelations(search);
notifyListeners();
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('listingType', _listingType);
}
void setSort(String value) async {
_sort = value;
getRelations(search);
notifyListeners();
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('sort', _sort);
}
/// Wraps [ScopedModel.of] for this [Model].
static RelationScopedModel of(BuildContext context) =>
ScopedModel.of<RelationScopedModel>(context);
}
I'm guessing that the problem is that my scopedmodeldescendant isn't in the init state. But i'm not sure and i hope somebody knows what the problem is with this code.
Thanks in advance
Greetings,
Jente
UPDATE:
I got my customscrollview is working when i set atTheEnd to false and hasMorePages to true in my relation_scoped_model at initializeValues().
The only problem i'm still facing is that i'm getting duplicates, which happens because my screen is rebuilding twice.
When using scoped model, you should wrap the widgets that should respond to changes one by one with scoped model descendants. This way only those widgets can rebuild rather than the whole page rebuilding.
Do someone know how to add dynamically more rows into the DataTable in Flutter. As you can see my code is very 'hardcoded' [line: 11-31].
There should be a way to get rid of writing more and more DataRows.
Code:
class DataTableWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DataTable(
columns: [
DataColumn(label: Text('Patch')),
DataColumn(label: Text('Version')),
DataColumn(label: Text('Ready')),
],
rows: <DataRow>[
DataRow(
cells: <DataCell>[
DataCell(Text('AAAAAA')),
DataCell(Text('1')),
DataCell(Text('Yes')),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('BBBBBB')),
DataCell(Text('2')),
DataCell(Text('No')),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('CCCCCC')),
DataCell(Text('3')),
DataCell(Text('Yes')),
],
),
],
);
}
}
You can use
listOfColumns.map(((element) => DataRow(...))).toList()
This is your code using this method.
class DataTableWidget extends StatelessWidget {
final List<Map<String, String>> listOfColumns = [
{"Name": "AAAAAA", "Number": "1", "State": "Yes"},
{"Name": "BBBBBB", "Number": "2", "State": "no"},
{"Name": "CCCCCC", "Number": "3", "State": "Yes"}
];
// DataTableWidget(this.listOfColumns); // Getting the data from outside, on initialization
#override
Widget build(BuildContext context) {
return DataTable(
columns: [
DataColumn(label: Text('Patch')),
DataColumn(label: Text('Version')),
DataColumn(label: Text('Ready')),
],
rows:
listOfColumns // Loops through dataColumnText, each iteration assigning the value to element
.map(
((element) => DataRow(
cells: <DataCell>[
DataCell(Text(element["Name"])), //Extracting from Map element the value
DataCell(Text(element["Number"])),
DataCell(Text(element["State"])),
],
)),
)
.toList(),
);
}
}
You could do something like this:**
class DataTableWidget extends StatelessWidget {
List results=[] ;
intState((){
super.iniState();
this.getSale();
})
Future<String> getData () async {
var response = await http.get(
"$saleUrl/?format=json",
);
setState(() {
var dataConvertedToJson =
json.decode(utf8.decode(response.bodyBytes));
results = dataConvertedToJson['results'];
});
print('${results.length}');
return "successful";
}
DataRow _getDataRow(result) {
return DataRow(
cells: <DataCell>[
DataCell(Text(data["text1"])),
DataCell(Text(data["text2"])),
DataCell(Text(data["text3"])),
],
);
}
#override
Widget build(BuildContext context) {
return DataTable(
columns: [
DataColumn(label: Text('Patch')),
DataColumn(label: Text('Version')),
DataColumn(label: Text('Ready')),
],
rows: List.generate(
results.length, (index) => _getDataRow(results[index])),
);
}
}
The underlying data structure, probably a list, is what you want to worry about changing. UI elements like the table just observe. The setState() function notifies the UI to take a fresh look after you've updated your list.
Here's a working example hacked from a flutter doc example. Click the button to add rows.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Add Rows',
home: MyHomePage(title: 'Add Rows'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<DataRow> _rowList = [
DataRow(cells: <DataCell>[
DataCell(Text('AAAAAA')),
DataCell(Text('1')),
DataCell(Text('Yes')),
]),
];
void _addRow() {
// Built in Flutter Method.
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below.
_rowList.add(DataRow(cells: <DataCell>[
DataCell(Text('BBBBBB')),
DataCell(Text('2')),
DataCell(Text('No')),
]));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: DataTable(columns: [
DataColumn(label: Text('Patch')),
DataColumn(label: Text('Version')),
DataColumn(label: Text('Ready')),
], rows: _rowList),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: _addRow,
label: Text('Add Row'),
backgroundColor: Colors.green,
),
);
}
}
This builds for me with
flutter build web
then run with
flutter run -d chrome
I am trying to build a dynamic TextField list in flutter but did not understand at how I can handle it.
My list looks like this:
My code structure is:
ListView.builder(
shrinkWrap: true,
itemCount: itemList.length,
itemBuilder: (context, index) {
if(itemList.length == null) {
return _buildProgressIndicator();
} else {
return singleItemList(index);
}
})
Widget singleItemList(int index) {
if((itemList.length -1) < index)
{
return Container(child: null);
} else {
dynamic singleItem = itemList[index];
String counter = (index+1).toString();
return
Container(
decoration: BoxDecoration(
color: Colors.white,
),
child: Row(
children:[
Expanded(
flex: 1,
child: Text(counter,style: orderBookItemListCounter(),)
),
Expanded(
flex: 3,
child: TextField(
controller: _addProductToCart(counter),
decoration: InputDecoration(
labelText: "Qty",
),
)
),
])
);
}
}
My product list is not fixed may change the number of products,
I would like to capture Qty as per product ID.
Thanks
:)
You have several options depending how you architecture your application or where you have your central state.
I propose you here a solution that updates a local map variable. Alternatively, you could send an event/stream to the place your store is located.
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Item> itemList = [
Item("ID1", "First product"),
Item("ID2", "Second product"),
];
Map<String, int> quantities = {};
void takeNumber(String text, String itemId) {
try {
int number = int.parse(text);
quantities[itemId] = number;
print(quantities);
} on FormatException {}
}
Widget singleItemList(int index) {
Item item = itemList[index];
return Container(
decoration: BoxDecoration(
color: Colors.white,
),
child: Row(
children: [
Expanded(flex: 1, child: Text("${index + 1}")),
Expanded(
flex: 3,
child: TextField(
keyboardType: TextInputType.number,
onChanged: (text) {
takeNumber(text, item.id);
},
decoration: InputDecoration(
labelText: "Qty",
),
),
),
],
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Demo")),
body: Center(
child: ListView.builder(
shrinkWrap: true,
itemCount: itemList.length,
itemBuilder: (context, index) {
if (itemList.isEmpty) {
return CircularProgressIndicator();
} else {
return singleItemList(index);
}
}),
),
);
}
}
class Item {
final String id;
final String name;
Item(this.id, this.name);
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<TextEditingController> _controllers = new List();
List<Item> itemList = [
Item("ID1", "product 1"),
Item("ID2", "product 2"),
Item("ID3", "product 3"),
Item("ID4", "product 4"),
Item("ID5", "product 5"),
Item("ID6", "product 6"),
Item("ID7", "product 7"),
Item("ID8", "product 8"),
Item("ID9", "product 9"),
Item("ID10", "product 10"),
Item("ID11", "product 11"),
Item("ID12", "product 12"),
Item("ID13", "product 13"),
Item("ID14", "product 14"),
Item("ID15", "product 15"),
Item("ID16", "product 16"),
Item("ID17", "product 17"),
Item("ID18", "product 18"),
];
Map<String, int> quantities = {};
void takeNumber(String text, String itemId) {
try {
int number = int.parse(text);
quantities[itemId] = number;
print(quantities);
} on FormatException {}
}
Widget singleItemList(int index, TextEditingController controllertxt) {
Item item = itemList[index];
return Container(
decoration: BoxDecoration(
color: Colors.white,
),
child: Row(
children: [
Expanded(flex: 1, child: Text("${index + 1}")),
Expanded(
flex: 3,
child: new TextField(
controller: controllertxt,
keyboardType: TextInputType.number,
onChanged: (text) {
takeNumber(text, item.id);
},
decoration: InputDecoration(
labelText: "Qty",
),
),
),
],
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Demo")),
body: Center(
child: ListView.builder(
shrinkWrap: true,
itemCount: itemList.length,
itemBuilder: (context, index) {
_controllers.add(new TextEditingController());
if (itemList.isEmpty) {
return CircularProgressIndicator();
} else {
return singleItemList(index,_controllers[index]);
}
}),
),
);
}
}
class Item {
final String id;
final String name;
Item(this.id, this.name);
}
I'm making one demo for learning purpose. In that demo I have just added ListView and when I tap on the ListTile there is Icons.favorite I just want to do Active and Deactive.
Below are my code.
import 'package:flutter/material.dart';
void main() => runApp(new RandomWordApp());
class RandomWordApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return new MaterialApp(
title: "Random App",
home: new RandomWordHome(),
);
}
}
class RandomWordHome extends StatefulWidget {
#override
createState() => new RandomState();
}
class RandomState extends State<RandomWordHome> {
#override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text("Random Word"),
),
body: setupListView(),
);
}
Widget setupListView() {
final _arrayOfData = [
{
"name": "Amit Patel",
"address": "Junagadh"
},
{"name": "Arjun Jain", "address": "Madhya Pradesh"},
{"name": "Ajay Shah", "address": "Limbadi"},
{"name": "Ankur Patel", "address": "Visanagar"},
{"name": "Soheb Ansari", "address": "Ahmedabad"}
];
final _arraySelectedRowStatus = List.filled(5, 0);
return new ListView.builder(
padding: new EdgeInsets.all(10.0),
itemCount: _arrayOfData.length,
itemBuilder: (context, i) {
return new ListTile(
title: new Text(_arrayOfData[i]["name"]),
subtitle: new Text(_arrayOfData[i]["address"]),
leading: new CircleAvatar(
backgroundColor: Colors.blue,
child: new Text(_arrayOfData[i]["name"][0])),
trailing: new Icon(
(_arraySelectedRowStatus[i] == 0
? Icons.favorite_border
: Icons.favorite),
color: (_arraySelectedRowStatus[i] == 0 ? null : Colors.red),
),
onTap: () {
setState(
() {
_arraySelectedRowStatus[i] =
(_arraySelectedRowStatus[i] == 0 ? 1 : 0);
Scaffold.of(context).showSnackBar(new SnackBar(
content:
new Text("You are selecting at " + i.toString())));
},
);
},
);
});
}
}
All are working well, even I got expected output from below line
print(_arrayOfData[i]["name"] + " - " + _arraySelectedRowStatus[i].toString());
But Icon is not change might be because ListView is not reloading. Where I'm going wrong? Guide me on right direction.
There are some issues with your approach
In order to update your UI, you have to use setState() method. Changing the variable is not enough.
You always create a final List inside ListView.builder block: final _arraySelectedRowStatus = List.filled(5, 0); and then use this list to render your UI. So the _arraySelectedRowStatus will always contain 0.
Below is updated code which will work
import 'package:flutter/material.dart';
void main() => runApp(new RandomWordApp());
class RandomWordApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return new MaterialApp(
title: "Random App",
home: new RandomWordHome(),
);
}
}
class RandomWordHome extends StatefulWidget {
#override
createState() => new RandomState();
}
class RandomState extends State<RandomWordHome> {
List _arraySelectedRowStatus = List.filled(5, 0);
#override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text("Random Word"),
),
body: setupListView(),
);
}
Widget setupListView() {
final _arrayOfData = [
{"name": "Amit Patel", "address": "Junagadh"},
{"name": "Arjun Jain", "address": "Madhya Pradesh"},
{"name": "Ajay Shah", "address": "Limbadi"},
{"name": "Ankur Patel", "address": "Visanagar"},
{"name": "Soheb Ansari", "address": "Ahmedabad"}
];
return new ListView.builder(
padding: new EdgeInsets.all(10.0),
itemCount: _arrayOfData.length,
itemBuilder: (context, i) {
return new ListTile(
title: new Text(_arrayOfData[i]["name"]),
subtitle: new Text(_arrayOfData[i]["address"]),
leading: new CircleAvatar(
backgroundColor: Colors.blue,
child: new Text(_arrayOfData[i]["name"][0])),
trailing: new Icon(
(_arraySelectedRowStatus[i] == 0
? Icons.favorite_border
: Icons.favorite),
color: (_arraySelectedRowStatus[i] == 0 ? null : Colors.red),
),
onTap: () {
_arraySelectedRowStatus[i] =
(_arraySelectedRowStatus[i] == 0 ? 1 : 0);
print(_arrayOfData[i]["name"] +
" - " +
_arraySelectedRowStatus[i].toString());
setState(() {
_arraySelectedRowStatus = _arraySelectedRowStatus;
});
Scaffold.of(context).showSnackBar(new SnackBar(
content: new Text("You are selecting at " + i.toString())));
},
);
});
}
}
[Update]
Here is the complete working code.
class DemoHelp extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
title: "Random App",
home: RandomWorldHome(),
);
}
}
class RandomWorldHome extends StatefulWidget {
#override
_RandomWorldHomeState createState() => new _RandomWorldHomeState();
}
class _RandomWorldHomeState extends State<RandomWorldHome> {
List<int> _arraySelectedRowStatus = List.filled(5, 1);
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("Random Word"),
),
body: setupListView(),
);
}
Widget setupListView() {
final _arrayOfData = [
{"name": "Amit Patel", "address": "Junagadh"},
{"name": "Arjun Jain", "address": "Madhya Pradesh"},
{"name": "Ajay Shah", "address": "Limbadi"},
{"name": "Ankur Patel", "address": "Visanagar"},
{"name": "Soheb Ansari", "address": "Ahmedabad"}
];
return ListView.builder(
padding: new EdgeInsets.all(10.0),
itemCount: _arrayOfData.length,
itemBuilder: (context, i) {
return ListTile(
title: Text(_arrayOfData[i]["name"]),
subtitle: new Text(_arrayOfData[i]["address"]),
leading: new CircleAvatar(
backgroundColor: Colors.blue,
child: new Text(_arrayOfData[i]["name"][0])),
trailing: new Icon(
(_arraySelectedRowStatus[i] == 0
? Icons.favorite_border
: Icons.favorite),
color: (_arraySelectedRowStatus[i] == 0 ? null : Colors.red),
),
onTap: (){
setState(() {
_arraySelectedRowStatus[i] = _arraySelectedRowStatus[i] == 0 ? 1 : 0;
});
Scaffold.of(context).showSnackBar(new SnackBar(
content:
new Text("You are selecting at ${_arraySelectedRowStatus[i]}")));
},
);
},
);
}
}
Couple of problems in your code.
variable _arraySelectedRowStatus is wrongly defined as final
state variable should be declared in the state class rather than in method.
setState((){}) is used like this.