How to view list of objects as widgets? - flutter

So i have a list of objects with different block_type such as title or text. This what i show in the app:
This is the code :
class AppView extends StatefulWidget {
final List tappedItems;
const AppView({Key? key, required this.tappedItems}) : super(key: key);
#override
_AppViewState createState() => _AppViewState();
}
class _AppViewState extends State<AppView> {
final _scaffoldKey = GlobalKey<ScaffoldState>();
final List listOfItems = [
{"block_type": "title", "block_data": "Books"},
{"block_type": "text", "block_data": "This is the textblock of books"},
{"block_type": "title", "block_data": "Publishers"},
{"block_type": "text", "block_data": "This is the textblock of publishers"},
];
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
key: _scaffoldKey,
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
leading: IconButton(
icon: const Icon(Icons.menu,
size: 40), // change this size and style
onPressed: () => _scaffoldKey.currentState?.openDrawer(),
),
actions: const [
Padding(
padding: EdgeInsets.fromLTRB(5, 20, 80, 5),
)
],
pinned: false,
expandedHeight: 100,
toolbarHeight: 100,
snap: true,
floating: true,
flexibleSpace: FlexibleSpaceBar(),
),
const SliverToBoxAdapter(),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return ListTile(
title: Text(
jsonEncode(listOfItems),
style: TextStyle(fontSize: 40, color: Colors.black),
),
);
},
childCount: 1,
),
),
],
),
drawer: const AppMenu(),
),
);
}
}
I also have different widgets for the blocks TitleBlock and TextBlock. This is an example for TitleBlock:
import 'package:flutter/cupertino.dart';
class TitleBlock extends StatefulWidget {
const TitleBlock({Key? key}) : super(key: key);
#override
_TitleBlockState createState() => _TitleBlockState();
}
class _TitleBlockState extends State<TitleBlock> {
#override
Widget build(BuildContext context) {
return Container();
}
}
I want show these seperate block widgets such as TitleBlock and TextBlock based on the block_type of the list of objects. I want to loop through the objects because this list of objects i showed is just an example. In my use case i can have different list of objects.
I want to show the blocks TitleBlock and TextBlock like this:

SliverChildBuilderDelegate gives you access to the builder, just like ListView.builder
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return ListTile(
title: Text(listOfItems[index]['block_type']),
subtitle: Text(listOfItems[index]['block_data']),
);
},
childCount: listOfItems.length
),
),

Based on your object's block_type you can call in different widgets with a ternary operator like in place of you ListTile, you can use the following,
listOfItems[index]["block_type"] == "title" ? TitleBlock(listOfItems[index]) : TextBlock(listOfItems[index])
And you can use the object i.e., Map<String, String> in the StatefulWidget (TitleBlock or TextBlock) to add any further info like,
class TitleBlock extends StatefulWidget {
const TitleBlock({Key? key, required this.dataMap}) : super(key: key);
final Map<String, String> dataMap;
#override
_TitleBlockState createState() => _TitleBlockState();
}
class _TitleBlockState extends State<TitleBlock> {
#override
Widget build(BuildContext context) {
return ListTile(
title: Text(widget.dataMap["block_type"]),
subtitle: Text(widget.dataMap["block_data"]),
);
}
}
class TextBlock extends StatefulWidget {
const TextBlock({Key? key, required this.dataMap}) : super(key: key);
final Map<String, String> dataMap;
#override
_TextBlockState createState() => _TextBlockState();
}
class _TextBlockState extends State<TextBlock> {
#override
Widget build(BuildContext context) {
return ListTile(
title: Text(widget.dataMap["block_type"]),
subtitle: Text(widget.dataMap["block_data"]),
);
}
}

Related

Why is the refreshing pull in the App not working?

I'm building my app with Flutter 2.10.5 and Dart 2.16.2.
When i try to refresh the demo content whith a pull, nothing happens. I have multiple navigation routes for different content. So the demo is a litte bit complex.
The main.dart includes the basic code for the app. I use the NavDrawer Widget to build the different pages. Every route is defined in the navigation.dart file, which reference to the content widgets.
My code so far is:
import 'dart:core';
import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
void main() async {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of the application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo Company',
theme: ThemeData(),
debugShowCheckedModeBanner: false,
home: const HomePage(title: 'Demo Company'),
);
}
}
class _HomePageState extends State<HomePage> {
#override
initState() {
super.initState();
}
Widget _infoTile(String title, String subtitle) {
return ListTile(
title: Text(title),
subtitle: Text(subtitle.isEmpty ? 'Not set' : subtitle),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: const NavDrawer(),
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
children: <Widget>[
_infoTile('App name', 'Demo App....'),
// Multiple Liste Tiles...
],
),
),
);
}
}
//----------------------------------------------------------------------
// navigation.dart
//----------------------------------------------------------------------
class NavDrawer extends StatelessWidget {
const NavDrawer({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const <Widget>[
Text(
'Navigation',
style: TextStyle(color: Colors.white, fontSize: 30),
),
SizedBox(height: 30.0),
Text('Firstname', style: TextStyle(color: Colors.black, fontSize: 15)),
Text('Accountname', style: TextStyle(color: Colors.black, fontSize: 15)),
],
),
),
ListTile(
leading: const Icon(Icons.notifications),
title: const Text('Demo'),
onTap: () {
Navigator.push(
context,
Demo.route(),
);
},
),
// Multiple Navigation List Tiles...
],
),
);
}
}
//----------------------------------------------------------------------
// demo.dart
//----------------------------------------------------------------------
class HomePage extends StatefulWidget {
const HomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<HomePage> createState() => _HomePageState();
}
class Demo extends StatefulWidget {
const Demo({Key? key}) : super(key: key);
static Route route() {
return CupertinoPageRoute(builder: (_) => const Demo());
}
#override
_DemoState createState() => _DemoState();
}
class _DemoState extends State<Demo> {
final _data = <WordPair>[];
#override
void initState() {
super.initState();
_data.addAll(generateWordPairs().take(20));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Woolha.com Flutter Tutorial'),
),
body: _buildList(),
);
}
Widget _buildList() {
return RefreshIndicator(
onRefresh: _refreshData,
child: ListView.builder(
padding: const EdgeInsets.all(20.0),
itemBuilder: (context, index) {
WordPair wordPair = _data[index];
return _buildListItem(wordPair.asString, context);
},
itemCount: _data.length,
),
);
}
Widget _buildListItem(String word, BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(word),
),
);
}
Future _refreshData() async {
await Future.delayed(const Duration(seconds: 3));
_data.clear();
_data.addAll(generateWordPairs().take(20));
setState(() {});
}
}
class ShowMessages extends StatelessWidget {
final String type;
final Color color;
const ShowMessages({Key? key, required this.type, required this.color}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView(
//color: color,
physics: const AlwaysScrollableScrollPhysics(),
children: [
ListTile(
title: Text(
type,
style: Theme.of(context).textTheme.bodyText1,
),
),
]);
}
}
Copy this code to DartPad
What is wrong?
Well for me this code... works
I copied it into Dartpad, then Dev Tools in browser (F12) > Device Emulation > Responsive. And you can use pull to refresh.
Of course this doesn't work using web view and mouse. I believe this gesture is not supported.

'RenderBox was not laid out' when using ExpansionPanelList

I want to show a GridView on the left and a ExpansionPanelList on the right. I put them in a Row Widget but it did not work, error is :
Assertion failed:
D:\…\rendering\box.dart:1929
hasSize
"RenderBox was not laid out: RenderRepaintBoundary#bb5e9 NEEDS-LAYOUT NEEDS-PAINT"
The relevant error-causing widget was
Row
lib\main.dart:69
this is all my code in main.dart:
// ignore_for_file: prefer_const_constructors
// ignore_for_file: prefer_const_literals_to_create_immutables
import 'package:flutter/material.dart';
void main() => runApp(MyApp(UniqueKey()));
class MyApp extends StatelessWidget {
const MyApp(Key key) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyHomePage(UniqueKey()),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage(Key key) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
List<int> getDataList() {
List<int> list = [];
for (int i = 0; i < 96; i++) {
list.add(i);
}
return list;
}
List<Widget> getWidgetList() {
return getDataList().map((item) => getItemContainer(item)).toList();
}
var i = 0;
Widget getItemContainer(int item) {
return Block(item);
}
//创建gridview
Widget buildGrid() {
return GridView.count(
//水平子Widget之间间距
crossAxisSpacing: 10.0,
//垂直子Widget之间间距
mainAxisSpacing: 30.0,
//GridView内边距
padding: EdgeInsets.all(10.0),
//一行的Widget数量
crossAxisCount: 4,
//子Widget宽高比例
childAspectRatio: 2.0,
//子Widget列表
children: getWidgetList(),
);
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("this is title"),
),
body: Container(
child: Row(
children: <Widget>[
Expanded(
child: buildGrid(),
),
ExpansionPanelPage(UniqueKey()),
],
)),
);
}
}
class Block extends StatelessWidget {
final int itemNo;
const Block(this.itemNo, {Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Listener(
onPointerHover: (event) => print(itemNo.toString()),
onPointerMove: (event) => print("aa" + itemNo.toString()),
child: Container(
alignment: Alignment.center,
child: Text(
"item",
style: TextStyle(color: Colors.white, fontSize: 20),
),
color: Colors.blue,
),
);
}
}
// stores ExpansionPanel state information
class Item {
Item({
required this.expandedValue,
required this.headerValue,
this.isExpanded = false,
});
String expandedValue;
String headerValue;
bool isExpanded;
}
List<Item> generateItems(int numberOfItems) {
return List.generate(numberOfItems, (int index) {
return Item(
headerValue: 'Panel $index',
expandedValue: 'This is item number $index',
);
});
}
class ExpansionPanelPage extends StatefulWidget {
ExpansionPanelPage(Key key) : super(key: key);
#override
_ExpansionPanelPageState createState() => _ExpansionPanelPageState();
}
class _ExpansionPanelPageState extends State<ExpansionPanelPage> {
List<Item> _data = generateItems(1);
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
width: 10,
height: 10,
child: _buildPanel(),
),
);
}
Widget _buildPanel() {
return ExpansionPanelList(
expansionCallback: (int index, bool isExpanded) {
setState(() {
_data[index].isExpanded = !isExpanded;
});
},
children: _data.map<ExpansionPanel>((Item item) {
return ExpansionPanelRadio(
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
title: Text(item.headerValue),
);
},
body: Column(
children :<Widget>[
Text("1"),
Text("1"),
Text("1"),
Text("1"),
]
),
value: item.headerValue,
);
}).toList(),
);
}
}
..........................................................................
..........................................................................
I know it might be a bit late to answer your question but for those who are facing the same issue, try wrapping ExpansionPanelList with the SingleChildScrollView widget. It helps your panel list to take all available space and to shrink-wrap in both axes. Check the docs for more info!

Consumer not updating the state?

I am trying to create an Icon with a number indicator on top of it and the number indicator receives its data via a Consumer provider. The problem is that the state is not being updated by the consumer function and I don't understand why (if I update the state with a hot reload everything works just fine).
Here is the code for my main file:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => TestData())
// I use more providers but deleted them here for brevity
],
child: TestScreen3(),
),
);
}
}
The test screen 3 widget
class TestScreen3 extends StatefulWidget {
#override
_TestScreen3State createState() => _TestScreen3State();
}
class _TestScreen3State extends State<TestScreen3> {
#override
Widget build(BuildContext context) {
final testData = Provider.of<TestData>(context);
return Scaffold(
appBar: AppBar(
title: Text('Test app 3'),
actions: [
Consumer<TestData>(builder: (_, data, __) {
return IconButton(
icon: Badge(num: data.items.length.toString()),
onPressed: () => print(data.items.length));
})
],
),
body: Center(
child: ElevatedButton(
child: Text('Increase'),
onPressed: () {
testData.addItem();
},
),
),
);
}
}
The badge widget
class Badge extends StatelessWidget {
Badge({#required this.num});
final String num;
#override
Widget build(BuildContext context) {
return Stack(
children: [
Icon(Icons.assessment),
Positioned(
child: Container(
padding: EdgeInsets.all(2),
child: Text(
num,
style: TextStyle(fontSize: 8),
textAlign: TextAlign.center,
),
constraints: BoxConstraints(
minHeight: 12,
minWidth: 12,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.red,
),
),
),
],
);
}
}
and the data model I am using
class Item {
Item(this.id);
final String id;
}
class TestData with ChangeNotifier {
List<Item> _items = [];
List<Item> get items => [..._items];
void addItem() {
_items.add(Item(DateTime.now().toString()));
}
notifyListeners();
}
The imports work just fine, I left them out for brevity. I followed along a this tutorial: https://www.udemy.com/course/learn-flutter-dart-to-build-ios-android-apps/ and it uses a key argument for the badge that looks like this:
class Badge extends StatelessWidget {
const Badge({
Key key,
#required this.child,
#required this.value,
this.color,
}) : super(key: key);
final Widget child;
final String value;
final Color color;
However, the use of key or super is not explained in the tutorial and when I add these parameters to my code they don't seem to make a change.
Many thanks in advance, I probably missed something super obvious...
Add notifyListeners(); inside addItem() method
void addItem() {
_items.add(Item(DateTime.now().toString()));
notifyListeners();
}

ListView in flutter with sticky side headers

How to implement a ListView with alphabet shown as sticky side headers, similarly to the Telegram application?
For example, while we look at the countries that begin with the letter "E", the letter "E" is not scrolling as long as there are countries that begin with the letter "F". Then the letter "E" is changed to the letter "F".
Solution with Sticky Side Header
You have a package for that! The flutter_sticky_header package.
Interesting points about the solution:
I used the diacritic package to remove the diacritics when indexing the list of Persons. You wouldn't want to exclude all of Norway's Øyvind, would you?
I used a SplayTreeMap to keep the Map sorted.
I used Flutter Hooks for the Persons mapping, happening only when contacts is changed.
Full source code
import 'dart:collection';
import 'package:diacritic/diacritic.dart';
import 'package:faker/faker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
part '66542479.alphabet.freezed.dart'; // File generated by freezed
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: DirectoryPage(contacts: dummyData),
),
);
}
class DirectoryPage extends HookWidget {
final List<Person> contacts;
const DirectoryPage({Key key, this.contacts}) : super(key: key);
#override
Widget build(BuildContext context) {
final _mappedContacts = useState<SplayTreeMap<String, List<Person>>>(null);
useEffect(() {
_mappedContacts.value = contacts.fold<SplayTreeMap<String, List<Person>>>(
SplayTreeMap<String, List<Person>>(),
(acc, curr) {
final firstChar = removeDiacritics(curr.name)[0];
acc..[firstChar] ??= [];
return acc..[firstChar].add(curr);
},
);
return;
}, [contacts]);
return Scaffold(
body: CustomScrollView(
slivers: _mappedContacts.value.keys
.map(
(firstChar) => _StickyHeaderList(
firstChar: firstChar,
persons: _mappedContacts.value[firstChar]),
)
.toList(),
),
);
}
}
class _StickyHeaderList extends StatelessWidget {
final String firstChar;
final List<Person> persons;
const _StickyHeaderList({
Key key,
this.firstChar,
this.persons,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return SliverStickyHeader(
overlapsContent: true,
header: _SideHeader(text: firstChar),
sliver: SliverPadding(
padding: const EdgeInsets.only(left: 60),
sliver: SliverList(
delegate: SliverChildListDelegate([
...persons.map((person) => PersonTile(person: person)).toList(),
Divider(),
]),
),
),
);
}
}
class _SideHeader extends StatelessWidget {
final String text;
const _SideHeader({
Key key,
this.text,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0),
child: Text(text, style: TextStyle(fontSize: 36.0)),
);
}
}
class PersonTile extends StatelessWidget {
final Person person;
const PersonTile({Key key, this.person}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListTile(title: Text(person.name));
}
}
final faker = Faker();
final dummyData = [
...List.generate(100, (index) => Person(name: faker.person.name())),
Person(name: 'Øyvind')
];
#freezed
abstract class Person with _$Person {
const factory Person({String name}) = _Person;
}
Here is a solution using Nested ListViews.
Interesting points about the solution:
I used the diacritic package to remove the diacritics when indexing the list of Persons. You wouldn't want to exclude all of Norway's Øyvind, would you?
I used a SplayTreeMap to keep the Map sorted.
I used Flutter Hooks for the ScrollController and the Persons mapping, happening only when contacts is changed.
Full source code
import 'dart:collection';
import 'package:diacritic/diacritic.dart';
import 'package:faker/faker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'my_file.freezed.dart'; // File generated by freezed
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: DirectoryPage(contacts: dummyData),
),
);
}
class DirectoryPage extends HookWidget {
final List<Person> contacts;
const DirectoryPage({Key key, this.contacts}) : super(key: key);
#override
Widget build(BuildContext context) {
final _scrollController = useScrollController();
final _mappedContacts = useState<SplayTreeMap<String, List<Person>>>(null);
useEffect(() {
_mappedContacts.value = contacts.fold<SplayTreeMap<String, List<Person>>>(
SplayTreeMap<String, List<Person>>(),
(acc, curr) {
final firstChar = removeDiacritics(curr.name)[0];
acc..[firstChar] ??= [];
return acc..[firstChar].add(curr);
},
);
return;
}, [contacts]);
return Scaffold(
body: Scrollbar(
controller: _scrollController,
isAlwaysShown: true,
child: ListView.separated(
controller: _scrollController,
itemBuilder: (context, index) {
final key = _mappedContacts.value.keys.elementAt(index);
return Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(key, style: TextStyle(fontSize: 36)),
),
const SizedBox(width: 8.0),
Expanded(
child: ListView(
shrinkWrap: true,
children: _mappedContacts.value[key]
.map((person) => PersonTile(person: person))
.toList(),
),
),
],
);
},
separatorBuilder: (_, __) => Divider(),
itemCount: _mappedContacts.value.length,
),
),
);
}
}
class PersonTile extends StatelessWidget {
final Person person;
const PersonTile({Key key, this.person}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListTile(title: Text(person.name));
}
}
final faker = Faker();
final dummyData = [
...List.generate(100, (index) => Person(name: faker.person.name())),
Person(name: 'Øyvind')
];
#freezed
abstract class Person with _$Person {
const factory Person({String name}) = _Person;
}

How to deselect the already selected item after tap on another item ListView in Flutter?

I would like to allow user to select only one option from the list, if he select one after another, then only last option should be considered as selected.
In current code, user can select multiple option from the list which i want to avoid.
Tried code:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Test App',
theme: new ThemeData(
primarySwatch: Colors.red,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<int> indexList = new List();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Selected ${indexList.length} ' + indexList.toString()),
),
body: new ListView.builder(
itemCount: 3,
itemBuilder: (context, index) {
return new CustomWidget(
index: index,
callback: () {
if (indexList.isNotEmpty) {
indexList.clear();
}
},
);
},
),
);
}
}
class CustomWidget extends StatefulWidget {
final int index;
final VoidCallback callback;
const CustomWidget({Key key, this.index, this.callback}) : super(key: key);
#override
_CustomWidgetState createState() => new _CustomWidgetState();
}
class _CustomWidgetState extends State<CustomWidget> {
bool selected = false;
CustomWidget lastSelectedWidget;
#override
Widget build(BuildContext context) {
return new GestureDetector(
onTap: () {
setState(() {
lastSelectedWidget = widget;
print(lastSelectedWidget.key);
selected = !selected;
});
widget.callback();
},
child: new Container(
margin: new EdgeInsets.all(5.0),
child: new ListTile(
title: new Text("Title ${widget.index}"),
subtitle: new Text("Description ${widget.index}"),
),
decoration: selected
? new BoxDecoration(color: Colors.black38, border: new Border.all(color: Colors.black))
: new BoxDecoration(),
),
);
}
}
I am implementing kind of radio button in list style.
You cannot assign the responsibility of defining which CustomWidget is selected to the own CustomWidget. A CustomWidget must not know about the existence of other CustomWidgets, neither anything about the information they hold.
Given that, your CustomWidget should be something like this:
class CustomWidget extends StatefulWidget {
final int index;
final bool isSelected;
final VoidCallback onSelect;
const CustomWidget({
Key key,
#required this.index,
#required this.isSelected,
#required this.onSelect,
}) : assert(index != null),
assert(isSelected != null),
assert(onSelect != null),
super(key: key);
#override
_CustomWidgetState createState() => _CustomWidgetState();
}
class _CustomWidgetState extends State<CustomWidget> {
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: widget.onSelect,
child: Container(
margin: EdgeInsets.all(5.0),
child: ListTile(
title: Text("Title ${widget.index}"),
subtitle: Text("Description ${widget.index}"),
),
decoration: widget.isSelected
? BoxDecoration(color: Colors.black38, border: Border.all(color: Colors.black))
: BoxDecoration(),
),
);
}
}
And the widget that uses CustomWidget:
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int currentSelectedIndex;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Selected index is $currentSelectedIndex'),
),
body: ListView.builder(
itemCount: 3,
itemBuilder: (context, index) {
return CustomWidget(
index: index,
isSelected: currentSelectedIndex == index,
onSelect: () {
setState(() {
currentSelectedIndex = index;
});
},
);
},
),
);
}
}