Flutter : Is it possible to create multiple elements in a expansion tile? - dart

I use Flutter and I would like to create a ExpansionTile List with 2 elements in the same Tile.
For example :
name :/.......White-Space....../ Snow.
firstname :/.......White-Space....../ John.
location :/.......White-Space....../ Winterfell.
But I don't know if it's possible.(after some search on internet and doc)
I've tried to work with Column, Row,.. in children's property but nothing is effective.
Someone have a solution ?
My Code :
class Entry{
const Entry(this.title,[this.children = const <Entry>[]]);
final String title;
final List<Entry> children;
}
const List<Entry> data = <Entry> [
Entry(
' Profile',
<Entry>[
Entry('Jhon'),
Entry('Snow'),
Entry('Winterfell'),
],
),
...
class Collapsible extends StatelessWidget{
#override
Widget build(BuildContext context) {
return ListView.builder(
itemBuilder: (BuildContext context, int index) => EntryItem(data[index]),
itemCount: data.length,
);
}
}
class EntryItem extends StatelessWidget{
const EntryItem(this.entry);
final Entry entry;
Widget _buildTiles(Entry root){
if(root.children.isEmpty) return ListTile(title: Text(root.title));
return ExpansionTile(
backgroundColor: Colors.white30,
initiallyExpanded: true,
key: PageStorageKey<Entry>(root),
title: Center(child: Text(root.title),),
children: root.children.map(_buildTiles).toList()
);
}
#override
Widget build(BuildContext context) {
return _buildTiles(entry);
}
}

(Edited)
Is this what you're looking for?
class Collapsible extends StatelessWidget{
#override
Widget build(BuildContext context) {
return ListView.builder(
itemBuilder: (context, i) {
if(i >= data.length) return null;
return ExpansionTile(
title: Text('name: ${data[i].title}'),
children: [
Text('firstname: ${data[i].children[0].title}'),
Text('firstname: ${data[i].children[1].title}'),
]
);
},
itemCount: data.length,
);
}
}

Related

Flutter - RepaintBoundary causes state reset of StatefulWidget

I have a preview widget that loads data after a user tap. This state (already tapped or not) should not be lost while scrolling (the preview is located in a list) or navigating through other screen.
The scrolling is solved by adding AutomaticKeepAliveClientMixin which saves the state when scrolling away.
Now i also need to wrap the preview widget (actually a more complex widget that contains the preview) with a RepaintBoundary, to be able to make a "screenshot" of this widget alone.
Before i wrap the widget with a RepaintBoundary, the state is saved both while scrolling and navigating to another screen.
After i add the RepaintBoundary the scrolling still works but for navigation the state is reset.
How can i wrap a Stateful widget that should hold its state with a RepaintBoundary?
Code is a simplified example of my implementation with the same problem.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
final title = 'Test';
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: TestList(40),
),
);
}
}
class TestList extends StatefulWidget {
final int numberOfItems;
TestList(this.numberOfItems);
#override
_TestListState createState() => _TestListState();
}
class _TestListState extends State<TestList> {
#override
Widget build(BuildContext context) {
print('_TestListState build.');
return ListView.builder(
itemCount: widget.numberOfItems,
itemBuilder: (context, index) {
return RepaintBoundary(
key: GlobalKey(),
child: Preview()
);
},
);
}
}
class Preview extends StatefulWidget {
#override
_PreviewState createState() => _PreviewState();
}
class _PreviewState extends State<Preview> with AutomaticKeepAliveClientMixin {
bool loaded;
#override
void initState() {
super.initState();
print('_PreviewState initState.');
loaded = false;
}
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context);
print('_PreviewState build.');
if(loaded) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => NewScreen()),
);
},
child: ListTile(
title: Text('Loaded. Tap to navigate.'),
leading: Icon(Icons.visibility),
),
);
} else {
return GestureDetector(
onTap: () {
setState(() {
loaded = true;
});
},
child: ListTile(
title: Text('Tap to load.'),
),
);
}
}
}
class NewScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('New Screen')),
body: Center(
child: Text(
'Navigate back and see if loaded state is gone.',
style: TextStyle(fontSize: 14.0),
),
),
);
}
}
Take a look at RepaintBoundary.wrap, it assigns the RepaintBoundary widget a key based on its child or childIndex so state is maintained:
class _TestListState extends State<TestList> {
#override
Widget build(BuildContext context) {
print('_TestListState build.');
return ListView.builder(
itemCount: widget.numberOfItems,
itemBuilder: (context, index) {
return RepaintBoundary.wrap(
Preview(),
index,
);
},
);
}
}
https://api.flutter.dev/flutter/widgets/RepaintBoundary/RepaintBoundary.wrap.html
EDIT: As per the below comments, it looks like this solution would break the screenshot ability so you'd have to store the list of children widgets in your state like so:
class _TestListState extends State<TestList> {
List<Widget> _children;
#override
void initState() {
super.initState();
_children = List.generate(
widget.numberOfItems,
(_) => RepaintBoundary(
key: GlobalKey(),
child: Preview(),
));
}
#override
Widget build(BuildContext context) {
print('_TestListState build.');
return ListView(children: _children);
}
}

Flutter: how to access ScopeModel properties in child pages

I am trying to understand ScopeModel in Flutter and need some help on how access values from the model on a different page
My home page has a bottom navigation bar and when click just display the search page. I have wrap the widget tree with the ScopeModel and added the model.
The count is getting incremented but I am not sure how to access it from the search page
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
ScopeCounter sc;
#override
void initState() {
super.initState();
sc = new ScopeCounter();
}
final List<Widget> _children = [
..
Search()
];
var _currentIndex = 0;
#override
Widget build(BuildContext context) {
return ScopedModel(
model:sc ,
child: Scaffold(
appBar: AppBar(
title: Center(child: Text("test")),
),
drawer: JobsDrawer(),
body: _children[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
onTap: onTabTapped, // new
currentIndex: _currentIndex, // new
items: [
new BottomNavigationBarItem(
icon: new Icon(Icons.search),
title: new Text("search"),
)
])),
);
}
void onTabTapped(int index) {
setState(() {
sc.increment();
print(sc.counter1.count);
_currentIndex = index;
});
}
}
This my Model
class ScopeCounter extends Model {
Counter counter1 = Counter();
increment() {
counter1.count += 1;
}
}
class Counter {
int count = 1;
}
Search page
class Search extends StatefulWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
...
}
}
I would like access the count from the "search" page.
Thanks for your help
You just have to wrap your SearchPage's Widget (Scaffold in this case) with a ScopedModelDescendant Widget. This gives you access to your ScopedModel.
A great explanation can be found in the documentation: https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple
EDIT: Also your ScopedModel must be a parent of both: MyHomePage and Search.

How to delete GridView item in flutter?

I'm trying to dynamically delete simple grid item on long press;
I've tried the most obvious way: created a list of grid data, and called setState on addition or deletion of the item.
UPD: Items works properly in the list, since it's initialisation loop moved to initState() method (just as #jnblanchard said in his comment), and don't generate new items at every build() call, but deletion is still doesn't work.
If it has more items, than can fit the screen, it deletes last row, (when enough items deleted), otherwise the following exception is thrown:
I/flutter (28074): The following assertion was thrown during performLayout():
I/flutter (28074): SliverGeometry is not valid: The "maxPaintExtent" is less than the "paintExtent".
I/flutter (28074): The maxPaintExtent is 540.0, but the paintExtent is 599.3. By definition, a sliver can't paint more
I/flutter (28074): than the maximum that it can paint!
My test code now:
main class
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:options_x_ray_informer/prototyping/TestTile.dart';
class Prototype extends StatefulWidget{
#override
_PrototypeState createState() => _PrototypeState();
}
class _PrototypeState extends State<Prototype> {
//list of grid data
List<Widget> gridItemsList = [];
#override
void initState(){
super.initState();
//----filling the list----
for(int i =0; i<10; i++){
gridItemsList.add(
TestTile(i, (){
//adding callback for long tap
delete(i);
})
);
}
}
#override
Widget build(BuildContext context) {
//----building the app----
return Scaffold(
appBar: AppBar(
title: Text("Prototype"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.add),
onPressed: () {
int index = gridItemsList.length+1;
add(
new TestTile(index, (){
delete(index);
})
);
},
),
]
),
body: GridView(
gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
children: gridItemsList
)
);
}
///method for adding the items
void add(Widget toAdd){
setState(() {
TestTile tile = toAdd as TestTile;
gridItemsList.add(toAdd);
print("tile number#${tile.index} added");
});
}
///method for deleting the items
void delete(int index){
setState(() {
gridItemsList.removeAt(index);
print("tile number#$index is deleted");
});
}
}
and separate widget class for grid items
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class TestTile extends StatelessWidget{
int _index;
var _callback;
TestTile(this._index, this._callback);
get index => _index;
#override
Widget build(BuildContext context) {
return GridTile(
child: Card(
child: InkResponse(
onLongPress: _callback,
child: Center(
child:Text("data#$_index")
)
)
),
);
}
}
How can I delete an item from grid view?
p.s. the provided code is just my attempts of solving the problem - you can offer another way, if you want!
I wrote this up from the example app, it has a few things that you may find useful. Notably I abstract the list data-structure by holding the length of the list inside a stateful widget. I wrote this with a ListView but I think you could change that to a GridView without any hiccups.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.indigo,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
#override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(
title: Text("Owl"),
actions: <Widget>[IconButton(icon: Icon(Icons.remove), onPressed: () => this.setState(() => _counter > 1 ? _counter-- : _counter = 0)), IconButton(icon: Icon(Icons.add), onPressed: () => this.setState(() => _counter++))],
),
body: ListView.builder(itemExtent: 50, itemCount: _counter, itemBuilder: (context, index) => Text(index.toString(), textAlign: TextAlign.center, style: Theme.of(context).textTheme.title))
);
}
}
Finally I've got what I wanted.
I'll leave it here for someone who might have the same problem :)
Main class:
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:options_x_ray_informer/prototyping/TestTile.dart';
class Prototype extends StatefulWidget{
#override
_PrototypeState createState() => _PrototypeState();
}
class _PrototypeState extends State<Prototype> {
//list of some data
List<Person> partyInviteList = [];
_PrototypeState(){
//filling the list
for(int i=0; i<5; i++){
partyInviteList.add(Person.generateRandomPerson());
}
print("Person ${partyInviteList.toString()}");
}
#override
Widget build(BuildContext context) {
//----building the app----
return Scaffold(
appBar: AppBar(
title: Text("Prototype"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.add),
//generating an item on tap
onPressed: () {
setState(() {
partyInviteList.add(Person.generateRandomPerson());
});
},
),
]
),
body: GridView.count(
crossAxisCount: 2,
children: List.generate(partyInviteList.length, (index) {
//generating tiles with people from list
return TestTile(
partyInviteList[index], (){
setState(() {
print("person ${partyInviteList[index]} is deleted");
partyInviteList.remove(partyInviteList[index]);
});
}
);
})
)
);
}
}
///person class
class Person{
Person(this.firstName, this.lastName);
static List<String> _aviableNames = ["Bob", "Alise", "Sasha"];
static List<String> _aviableLastNames = ["Green", "Simpson", "Stain"];
String firstName;
String lastName;
///method that returns random person
static Person generateRandomPerson(){
Random rand = new Random();
String randomFirstName = _aviableNames[rand.nextInt(3)];
String randomLastName = _aviableLastNames[rand.nextInt(3)];
return Person(randomFirstName, randomLastName);
}
#override
String toString() {
return "$firstName $lastName";
}
}
Support class:
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:options_x_ray_informer/prototyping/Prototype.dart';
class TestTile extends StatelessWidget{
final Person person;
var _callback;
TestTile(this.person, this._callback);
#override
Widget build(BuildContext context) {
return GridTile(
child: Card(
child: InkResponse(
onLongPress: _callback,
child: Center(
child:Text("${person.toString()}")
)
)
),
);
}
}

Single Instance of Widget class is used even when different variables are coded to be used

I want to use BottomNavigationBar of Flutter so for that I have created a class called BaseWidget which will be changed as the user taps the item.
class BaseWidget extends StatefulWidget {
final String title;
BaseWidget(this.title);
_BaseWidgetState createState() => _BaseWidgetState(this.title);
}
class _BaseWidgetState extends State<BaseWidget> {
final String title;
_BaseWidgetState(this.title);
#override
Widget build(BuildContext context) {
return Center(child: Text(title));
}
}
In the above class am returning the Center widget with child as Text widget.
class HomeWidget extends StatefulWidget {
_HomeWidgetState createState() => _HomeWidgetState();
}
class _HomeWidgetState extends State<HomeWidget> {
int pageIndex = 0;
final _home = BaseWidget('Home');
final _business = BaseWidget('Business');
final _school = BaseWidget('School');
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Bottom Navigation Bar'),
),
body: choosePager(),
bottomNavigationBar: BottomNavigationBar(
currentIndex: pageIndex,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home), title: Text('Home')),
BottomNavigationBarItem(
icon: Icon(Icons.business), title: Text('Business')),
BottomNavigationBarItem(
icon: Icon(Icons.school), title: Text('School')),
],
onTap: onTap,
),
),
);
}
void onTap(int index) {
setState(() {
this.pageIndex = index;
});
}
Widget choosePager() {
switch (pageIndex) {
case 0:
return _home;
break;
case 1:
return _business;
break;
case 2:
return _school;
break;
default:
return Text('Unknown');
break;
}
}
}
Problem 1:
Whenever user taps on the BottomNavigationBarItem the text should change to the respected string passed in the BaseWidget's constructor. But it only shows Home and the rest 2 are ignored.
Problem 2:
I am planning to replace Center widget with the ListView widget to populate the list of Schools and Businesses which will be fetched from the network API in paginated way. So I don't want to reinitialise the classes again when BottomNavigationBarItem is tapped as that would result in loss of data which is already fetched. To prevent data lose I am declaring _home, _business & _school property and using these property in choosePager() method.
There are several issues with your code:
1- The real problem is that you never rebuild the BaseWidget. You construct 3 new BaseWidgets, but you only ever call the build of the _home widget, because it's the first one returned by choosePager(). Since you don't create _home, _business, _school in the HomeWidget build, no other BaseWidget can ever get built.
2- When you don't need to store any state/variables for a widget, use a Stateless widget.
3- Don't do anything in the constructor of your State. Use initState https://docs.flutter.io/flutter/widgets/State/initState.html for that instead.
4- Create widgets using const constructors when possible.
5- Widget constructor take named parameters. One of those should be the key. Use super to call the base constructor.
With that in mind, this is what the code should look like:
class BaseWidget extends StatelessWidget {
final String title;
const BaseWidget({Key key, this.title}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: Text(title),
);
}
}
class HomeWidget extends StatefulWidget {
_HomeWidgetState createState() => _HomeWidgetState();
}
class _HomeWidgetState extends State<HomeWidget> {
int pageIndex = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Bottom Navigation Bar'),
),
body: choosePager(),
bottomNavigationBar: BottomNavigationBar(
currentIndex: pageIndex,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
title: Text('Business'),
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
title: Text('School'),
),
],
onTap: onTap,
),
),
);
}
void onTap(int index) {
setState(() {
pageIndex = index;
});
}
Widget choosePager() {
Widget result;
switch (pageIndex) {
case 0:
result = BaseWidget(title: 'Home');
break;
case 1:
result = BaseWidget(title: 'Business');
break;
case 2:
result = BaseWidget(title: 'School');
break;
default:
result = Text('Unknown');
break;
}
return result;
}
}
Edit: For your example, you may want to fetch some data from the network and only use the widget to display it. In that case, create a new class (not a Widget) to fetch & hold on to the data, and use the Widget only for displaying the data.
Some sample code:
/// Silly class to fetch data
class DataClass {
static int _nextDatum = 0;
int _data;
DataClass();
Future<int> fetchData() async {
await Future.delayed(Duration(
milliseconds: 2000,
));
_data = _nextDatum++;
return _data;
}
int getData() {
return _data;
}
}
class BaseClass extends StatefulWidget {
final String title;
final DataClass data;
const BaseClass({Key key, this.title, this.data}) : super(key: key);
#override
_BaseClassState createState() => _BaseClassState();
}
class _BaseClassState extends State<BaseClass> {
String title;
DataClass data;
#override
Widget build(BuildContext context) {
String dataStr = data == null ? ' - ' : '${data.getData()}';
return Center(
child: Text(
'$title: $dataStr',
),
);
}
#override
void initState() {
super.initState();
// initState gets called only ONCE
title = widget.title;
data = widget.data;
}
#override
void didUpdateWidget(BaseClass oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.data != oldWidget.data) {
data = widget.data;
}
if (widget.title != oldWidget.title) {
title = widget.title;
}
}
}
class HomeWidget extends StatefulWidget {
_HomeWidgetState createState() => _HomeWidgetState();
}
class _HomeWidgetState extends State<HomeWidget> {
int pageIndex = 0;
Map<String, DataClass> _dataMap = <String, DataClass>{};
#override
void initState() {
super.initState();
_init().then((result) {
// Since we need to rebuild the widget with the resulting data,
// make sure to use `setState`
setState(() {
_dataMap = result;
});
});
}
Future<Map<String, DataClass>> _init() async {
// this fetches the data only once
return <String, DataClass>{
'home': DataClass()..fetchData(),
'business': DataClass()..fetchData(),
'school': DataClass()..fetchData(),
};
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Bottom Navigation Bar'),
),
body: choosePager(),
bottomNavigationBar: BottomNavigationBar(
currentIndex: pageIndex,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
title: Text('Business'),
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
title: Text('School'),
),
],
onTap: onTap,
),
),
);
}
void onTap(int index) {
setState(() {
pageIndex = index;
});
}
Widget choosePager() {
Widget result;
switch (pageIndex) {
// it doesn't matter if you create a new BaseClass() a hundred times, flutter is optimized enough to not care. The `initState()` gets called only once. You're fetching the data only once.
case 0:
result = BaseClass(
title: 'Home',
data: _dataMap['home'],
);
break;
case 1:
result = BaseClass(
title: 'Business',
data: _dataMap['business'],
);
break;
case 2:
result = BaseClass(
title: 'School',
data: _dataMap['school'],
);
break;
default:
result = Text('Unknown');
break;
}
return result;
}
}
After lot of RND I solved my problem using IndexedStack. It shows the single Child from the list of children based on the index.
It will initialise all the children when the Widget build(BuildContext context) method of the _HomeWidgetState is called. So any time you switch the tabs, the object won't be reinitialised.
Here is my full code
class BaseWidget extends StatefulWidget {
final String title;
BaseWidget(this.title);
_BaseWidgetState createState() => _BaseWidgetState();
}
class _BaseWidgetState extends State<BaseWidget> {
#override
Widget build(BuildContext context) {
return Center(child: Text(widget.title));
}
}
class HomeWidget extends StatefulWidget {
_HomeWidgetState createState() => _HomeWidgetState();
}
class _HomeWidgetState extends State<HomeWidget> {
int _pageIndex = 0;
List<Widget> _children;
#override
void initState() {
super.initState();
_children = [
BaseWidget('Home'),
BaseWidget('Business'),
BaseWidget('School')
];
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Bottom Navigation Bar'),
),
body: IndexedStack(
children: _children,
index: _pageIndex,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _pageIndex,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home), title: Text('Home')),
BottomNavigationBarItem(
icon: Icon(Icons.business), title: Text('Business')),
BottomNavigationBarItem(
icon: Icon(Icons.school), title: Text('School')),
],
onTap: onTap,
),
),
);
}
void onTap(int index) {
setState(() {
_pageIndex = index;
});
}
}
Please try this code, I have edited the BaseWidget class
class BaseWidget extends StatefulWidget {
final String title;
BaseWidget(this.title);
_BaseWidgetState createState() => _BaseWidgetState();
}
class _BaseWidgetState extends State<BaseWidget> {
#override
Widget build(BuildContext context) {
return Center(child: Text(widget.title));
}
}

Undefined name with hero widget in flutter

I'm trying to work with hero widget .. every thing working fine.. my problem the tag for hero should be unique .. for the main scaffold i can make it unique by using the id from my api .. but i can't pass this id to the second Scaffold ... it become undefined .. how i can defined it ,,,
My Code is
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:json/add.dart';
Future<List> getData() async {
String url = 'http://192.168.0.57:4000/api/contacts';
http.Response response = await http.get(url);
return json.decode(response.body);
}
List data;
void main() async {
data = await (getData());
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
routes: <String, WidgetBuilder>{
'/Add': (BuildContext context) => new Add(),
},
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return HomePageState();
}
}
class HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
// TODO: implement build
return new MaterialApp(
title: "Test",
home: new Scaffold(
appBar: AppBar(
centerTitle: true,
title: new Text("Chat"),
),
body: new Center(
child: new ListView.builder(
itemCount: data.length,
itemBuilder: (BuildContext context, int position) {
return new ListTile(
title: new Text('${data[position]['name']}'),
subtitle: new Text('${data[position]['email']}'),
leading: new InkWell(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return HeroPage();
}));
},
child: Hero(
tag: "${data[position]['id']}",
child: new CircleAvatar(
child: new Text("${data[position]['name'][0]}"),
),
),
),
onTap: () {},
);
}),
),
),
);
}
}
class HeroPage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return HeroPageState();
}
}
class HeroPageState extends State<HeroPage> {
#override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: AppBar(),
body: Hero(
tag: "${data[position]['id']}",
child: new Container(
color: Colors.blueAccent,
),
),
);
}
}
You can Pass the Position(Int) with help of Class Constructors.
class HeroPage extends StatefulWidget {
final int position;
final List data;
HeroPage({this.position,this.data});
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return HeroPageState();
}
}
class HeroPageState extends State<HeroPage> {
#override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: AppBar(),
body: Hero(
tag: "${widget.data[widget.position]['id']}",
child: new Container(
color: Colors.blueAccent,
),
),
);
}
}
Call the page like in your InkWell onTap::
Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return HeroPage(position: position,data: data);
In the another page, try to wrap you hero in a ListView.builder, but the trick is only to set 1 in the itemCount param, with this, you can manipulate to show only one and get the correct tag

Resources