I want to display the JSON data inside ListView.builder received from the previous screen. Below is the sample code till now that I have tried.
FirstPage.dart
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => MyOrderDetails(
storeItems: order.inDetail!.menuItems!
)));
This is the sample json i am passing to Next Screen
{
"item_name": "Test",
"quantity": 1,
"subtotal": "434.78"
}
MyOrderDetail.dart
class MyOrderDetails extends StatefulWidget {
final List storeItems;
const MyOrderDetails(
{Key? key,
required this.storeItems})
: super(key: key);
#override
State<MyOrderDetails> createState() => _MyOrderDetailsState();
}
class _MyOrderDetailsState extends State<MyOrderDetails> {
#override
Widget build(BuildContext context) {
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
var lang = translator.activeLanguageCode;
return Scaffold(
appBar: AppBar(
elevation: 0,
),
body: ListView(
children: [
ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: widget.storeItems.length,
itemBuilder: (BuildContext context, int index) {
return Text(widget.storeItems[index]['item_name']); // Getting error here
}),
],
),
);
}
}
Your variable should be like that. List is not enough by itself you should declare which class is for that list.
final List<YourDataClass> storeItems;
const MyOrderDetails(
{Key? key,
required this.storeItems})
: super(key: key);
Related
I have a valueNotifier that generates a list of events and takes a random string every 5 seconds and sends it to the screen. It lies in inheritedWidget. How can I display in the ListView the event that came with the valueNotifier? What is the correct way to print the answer?
My code:
class EventList extends StatelessWidget {
const EventList({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return EventInherited(
child: EventListScreen(),
);
}
}
class EventListScreen extends StatefulWidget {
const EventListScreen({Key? key}) : super(key: key);
#override
State<EventListScreen> createState() => _EventListScreenState();
}
class _EventListScreenState extends State<EventListScreen> {
#override
Widget build(BuildContext context) {
final eventNotifier = EventInherited.of(context).eventNotifier;
return Scaffold(
appBar: AppBar(
title: const Text('Event List'),
centerTitle: true,
),
body: Container(
padding: const EdgeInsets.all(30),
child: ValueListenableBuilder(
valueListenable: eventNotifier,
builder: (BuildContext context, List<String> value, Widget? child) {
return ListView(
children: [
],
);
},
),
),
);
}
}
class EventNotifier extends ValueNotifier<List<String>> {
EventNotifier(List<String> value) : super(value);
final List<String> events = ['add', 'delete', 'edit'];
final stream = Stream.periodic(const Duration(seconds: 5));
late final streamSub = stream.listen((event) {
value.add(
events[Random().nextInt(4)],
);
});
}
class EventInherited extends InheritedWidget {
final EventNotifier eventNotifier = EventNotifier([]);
EventInherited({required Widget child}) : super(child: child);
static EventInherited of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType()!;
}
#override
bool updateShouldNotify(EventInherited oldWidget) {
return oldWidget.eventNotifier.streamSub != eventNotifier.streamSub;
}
}
If you have correct value, you can return listview like this:
return ListView.builder(
itemCount: value.length,
itemBuilder: (context, index) {
return Text(value[index]);
},
);
After having a quick look at ValueNotifier,
It says the following:
When the value is replaced with something that is not equal to the old value as evaluated by the equality operator ==, this class notifies its listeners.
In your case, the value is an array. By adding items to the array, it wont recognise a change.
Also see other Stacko post.
Try something like:
value = [...value].add(...)
It's possible to jump to specific item by item data in ListView?
class Test extends StatelessWidget {
Test({Key? key}) : super(key: key);
final _list = <String>[
"INFWARS_CH01_EP01",
"INFWARS_CH01_EP02",
"INFWARS_CH01_EP03",
"INFWARS_CH01_EP04",
"INFWARS_CH01_EP05",
];
void _scrollToItem() {
final specificItem = "INFWARS_CH01_EP04";
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: _list.length,
itemBuilder: (context, index) {
final data = _list[index];
return Text(data);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => _scrollToItem(),
),
);
}
}
as you can see, I want to jump to specific item in ListView by specific data "INFWARS_CH01_EP04" using _scrollToItem function, not by index or by position.
So the item ListView for INFWARS_CH01_EP04 will be in the top (scrolled). For now in the top is INFWARS_CH01_EP01.
It's possible to do it?
I fix it using this package: https://pub.dev/packages/scroll_to_index
So you can scroll / jump to specific item by index / by item data in ListView.
class Test extends StatelessWidget {
Test({Key? key}) : super(key: key);
AutoScrollController _scrollController = AutoScrollController();
final _list = <String>[
"INFWARS_CH01_EP01",
"INFWARS_CH01_EP02",
"INFWARS_CH01_EP03",
"INFWARS_CH01_EP04",
];
void _scrollToItem() async {
final specificItem = "INFWARS_CH01_EP04";
final index = _list.indexOf(specificItem);
await _scrollController.scrollToIndex(
index,
preferPosition: AutoScrollPosition.begin,
);
await _scrollController.highlight(index);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
controller: _scrollController,
itemCount: _list.length,
itemBuilder: (context, index) {
final data = _list[index];
return AutoScrollTag(
key: ValueKey(index),
controller: _scrollController,
index: index,
child: Text(data),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => _scrollToItem(),
),
);
}
}
To scroll to a specific item you can:
Find the specific item using the indexOf() method:
Use the scrollable_positioned_list package to scroll to that item.
Here is a complete working example:
class Test extends StatelessWidget {
Test({Key? key}) : super(key: key);
ItemScrollController _scrollController = ItemScrollController();
final _list = <String>[
"INFWARS_CH01_EP01",
"INFWARS_CH01_EP02",
"INFWARS_CH01_EP03",
"INFWARS_CH01_EP04",
];
void _scrollToItem() {
final specificItem = "INFWARS_CH01_EP04";
_scrollController.jumpTo(index: _list.indexOf(specificItem));
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ScrollablePositionedList.builder(
itemScrollController: _scrollController,
itemCount: _list.length,
itemBuilder: (context, index) {
final data = _list[index];
return Text(data);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => _scrollToItem(),
),
);
}
}
See also: flutter ListView scroll to index not available
You can use the flutter_scrollview_observer lib to implement your desired functionality without invasivity
Create and use instance of ScrollController normally.
ScrollController scrollController = ScrollController();
ListView _buildListView() {
return ListView.separated(
controller: scrollController,
...
);
}
Create an instance of ListObserverController pass it to ListViewObserver
ListObserverController observerController = ListObserverController(controller: scrollController);
ListViewObserver(
controller: observerController,
child: _buildListView(),
...
)
Now you can scroll to the specified index position
// Find the specific item index.
final targetIndex = _list.indexOf(specificItem);
// Jump to the specified index position without animation.
observerController.jumpTo(index: targetIndex)
// Jump to the specified index position with animation.
observerController.animateTo(
index: targetIndex,
duration: const Duration(milliseconds: 250),
curve: Curves.ease,
);
After upgrade my Flutter app is now producing this error at
return BaseWidget<BillsModel>(
The named parameter child is required but there's no corresponding
argument.
My BaseWidget has a child parameter but I don't know how to specify the child. This code previously worked but now doesn't. I realise there are many similar questions but they are sufficiently different that I can't figure this out. I have many similar errors in my project which all extend from BaseWidget
class Bills extends StatelessWidget {
const Bills({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
Tbl _table = Provider.of<Tbl>(context, listen: false);
return BaseWidget<BillsModel>(
model: BillsModel(api: Provider.of(context, listen: false)),
onModelReady: (model) => model.fetchBills(context, _table.id),
builder: (context, model, child) => model.busy
? Center(
child: CircularProgressIndicator(),
)
: Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: model.bills.length,
itemBuilder: (context, index) => BillListItem(
bill: model.bills[index],
),
)
)
);
}
}
Here is my BillsModel
class BillsModel extends BaseModel {
Api _api;
BillsModel({required Api api}) : _api = api;
List<Bill> bills = [];
Future fetchBills(BuildContext context, int tableId) async {
setBusy(true);
bills = await _api.getBills(context, tableId);
setBusy(false);
}
...
#override
void dispose() {
print('Bills has been disposed!!');
super.dispose();
}
}
Here is my BaseWidget:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class BaseWidget<T extends ChangeNotifier> extends StatefulWidget {
final Widget Function(BuildContext context, T model, Widget? child) builder;
final T model;
final Widget child;
final Function(T) onModelReady;
BaseWidget({
Key? key,
required this.builder,
required this.model,
required this.child,
required this.onModelReady,
}) : super(key: key);
_BaseWidgetState<T> createState() => _BaseWidgetState<T>();
}
class _BaseWidgetState<T extends ChangeNotifier> extends State<BaseWidget<T>> {
late T model;
#override
void initState() {
model = widget.model;
widget.onModelReady(model);
super.initState();
}
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<T>(
create: (context) => model,
child: Consumer<T>(
builder: widget.builder,
child: widget.child,
),
);
}
}
You should pass child parameters with any widget as your BaseWidget according to BaseWidget class.
Add an example code line, check it please
class Bills extends StatelessWidget {
const Bills({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
Tbl _table = Provider.of<Tbl>(context, listen: false);
return BaseWidget<BillsModel>(
model: BillsModel(api: Provider.of(context, listen: false)),
onModelReady: (model) => model.fetchBills(context, _table.id),
child: const Sizedbox.shrink(), // Added This Line !
builder: (context, model, child) => model.busy
? Center(
child: CircularProgressIndicator(),
)
: Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: model.bills.length,
itemBuilder: (context, index) => BillListItem(
bill: model.bills[index],
),
)
)
);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
const SliverToBoxAdapter(),
Consumer(
builder: (context, watch, child) {
return watch(recordProvider).when(
data: (records) {
// how to returns a set of Widgets(SliverPersistentHeader and SliverList) based on each item of records,
},
loading: () => const SliverToBoxAdapter(),
error: (obj, error) => const SliverToBoxAdapter(),
);
},
),
const SliverToBoxAdapter(),
],
),
);
}
}
I am new to flutter, want to know how to returns a set of Widgets(SliverPersistentHeader and SliverList) based on each item of records in List? thanks!
Howdy and welcome to Flutter. Slivers are a lot more complicated than using the other higher level widgets so bare with me on this answer. The code is documented with what's going on and it's using a very primitive list of data to build the CustomScrollView, just a List<int>.
/// fake FutureProvider
final recordProvider = FutureProvider(
(ref) => Future.value(
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
),
);
/// Our page of records
class RecordPage extends StatelessWidget {
const RecordPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Consumer(
builder: (context, watch, child) {
/// Building a list of our slivers up front so that it's easier to read
final List<Widget> slivers = watch(recordProvider).when(
data: (records) {
/// Lets split up some data so we can have our persistent headers hold some lists
final even = records.where((element) => element & 1 == 0);
final odd = records.where((element) => element & 1 == 1);
/// Now we need to take that data and make some lists of widgets
final evenWidgets =
even.map((e) => RecordWidget(record: e)).toList();
final oddWidgets =
odd.map((e) => RecordWidget(record: e)).toList();
/// Now let's make some SliverLists
final evenSliverList = SliverList(
delegate: SliverChildListDelegate.fixed(evenWidgets),
);
final oddSliverList = SliverList(
delegate: SliverChildListDelegate.fixed(oddWidgets),
);
/// Now let's create some headers
const evenHeader = SliverPersistentHeader(
delegate: RecordPersistentHeader('Even'),
);
const oddHeader = SliverPersistentHeader(
delegate: RecordPersistentHeader('Odd'),
);
/// Now let's merge them all together and return the slivers
return [
evenHeader,
evenSliverList,
oddHeader,
oddSliverList,
];
},
loading: () => [const SliverToBoxAdapter()],
error: (obj, error) => [const SliverToBoxAdapter()],
);
/// Finally let's return our [CustomScrollView] with our list of slivers
return CustomScrollView(
slivers: slivers,
);
},
),
);
}
}
/// The contents of our record sliver
class RecordWidget extends StatelessWidget {
const RecordWidget({Key? key, required this.record}) : super(key: key);
final int record;
#override
Widget build(BuildContext context) {
return Text('$record');
}
}
/// The contents of our header sliver
class RecordPersistentHeader extends SliverPersistentHeaderDelegate {
const RecordPersistentHeader(this.title);
final String title;
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return Text(title);
}
#override
double get maxExtent => 40;
#override
double get minExtent => 40;
#override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
return false;
}
}
I have the following issue with my 'workout' App using multiple workoutlists with various workoutitems:
I select a workoutlist with 12 workoutitems.
The 'activity' screen with the AnimatedList is shown.
Afterwards, I select a different workoutlist with 80 workoutitems.
The AnimatedList is now showing the new workoutlist but only the first 12 workoutitems.
Why?
I thought that the AnimatedList inside the build Widget is rebuild every time (I am not using GlobalKey).
class WorkoutListView extends StatelessWidget {
const WorkoutListView({this.filename});
final String filename;
#override
Widget build(BuildContext context) {
return Selector<WorkoutListModel, List<Workout>>(
selector: (_, model) => model.filterWorkouts(filename),
builder: (context, workouts, _) {
return AnimatedWorkoutList(
list: workouts,
);
},
);
}
}
class AnimatedWorkoutList extends StatefulWidget {
const AnimatedWorkoutList({
Key key,
#required List<Workout> list,
}) : _list = list,
super(key: key);
final List<Workout> _list;
#override
_AnimatedWorkoutListState createState() => _AnimatedWorkoutListState();
}
class _AnimatedWorkoutListState extends State<AnimatedWorkoutList> {
#override
Widget build(BuildContext context) {
return AnimatedList(
initialItemCount: widget._list.length,
itemBuilder: (context, index, animation) {
final workout = widget._list[index];
return Column(
children: [
// Using AnimatedList.of(context).removeItem() for list manipulation
],
);
},
);
}
}
try this:
class AnimatedWorkoutList extends StatefulWidget {
const AnimatedWorkoutList({
#required List<Workout> list,
});
final List<Workout> list;
#override
_AnimatedWorkoutListState createState() => _AnimatedWorkoutListState();
}
class _AnimatedWorkoutListState extends State<AnimatedWorkoutList> {
#override
Widget build(BuildContext context) {
return AnimatedList(
initialItemCount: widget.list.length,
itemBuilder: (context, index, animation) {
final workout = widget.list[index];
return Column(
children: [
// Using AnimatedList.of(context).removeItem() for list manipulation
],
);
},
);
}
}