Adding a image to an expansion tile - flutter

I want to add an image as a leading widget to my expansion tile. Every entry of my List has a different image. I added the image path to every entry of my list but the image is not displayed.
This is my List:
class Entry{
final String title;
final String imageUrl;
final List<Entry> children;
Entry(this.title, this.imageUrl, [this.children = const <Entry>[]]);
}
final List<Entry> data = <Entry>[
Entry(
'title', "/assets/images/picture.png",
<Entry>[
Entry(
'subtitle', '/assets/images/picture.jpg',
<Entry>[
Entry('subtitle', '/assets/images/example.jpg' ),
Entry('subtitle', '/assets/images/example.jpg' ),
Entry('subtitle', '/assets/images/example.jpg' ),
],
),
],
),
];
this is the code of the expansion tile:
import 'package:flutter/material.dart';
import '../models/entry.dart';
class EntryItem extends StatelessWidget {
const EntryItem(this.entry);
final Entry entry;
Widget _buildTiles(Entry root) {
if (root.children.isEmpty) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
border: Border.all(
width: 2,
color: Color.fromRGBO(3, 120, 163, 1),
),
),
child: ListTile(
leading: Image.asset(root.imageUrl),
title: Text(
root.title,
),
),
),
);
}
return Padding(
padding: const EdgeInsets.all(10.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
border: Border.all(
width: 3,
color: Color.fromRGBO(3, 120, 163, 1),
),
),
child: ExpansionTile(
key: PageStorageKey<Entry>(root),
leading: Image.asset(root.imageUrl),
title: Text(root.title, style: TextStyle(color: Colors.black)),
children: root.children.map<Widget>(_buildTiles).toList(),
),
),
);
}
#override
Widget build(BuildContext context) {
return _buildTiles(entry);
}
}
adding the image path directly to the leading widget works but displays the same image for all tiles.
Thank you in advance.

Related

How to create UI like this in Flutter

I'm building an App I have to build UI like below but I don't have any idea that how to create UI like this. Kindly guide me through this.
var listImages = [
"https://source.unsplash.com/random/200x200?sig=1",
"https://source.unsplash.com/random/200x200?sig=2",
"https://source.unsplash.com/random/200x200?sig=3",
"https://source.unsplash.com/random/200x200?sig=4",
"https://source.unsplash.com/random/200x200?sig=5"
];
Padding(
padding: const EdgeInsets.all(8.0),
child: Stack(
children: [
for (int i = 0; i < (listImages.length>=4?4:listImages.length); i++)
Transform.translate(
offset: Offset(i == 0 ? 0.0 : i * 44, 0),
child: Container(
height: 70,
width: 70,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
color: Colors.black87,
),
clipBehavior: Clip.hardEdge,
child: (i+1)>=4?Text("+ ${listImages.length-3}",style:const TextStyle(color: Colors.green),):Image.network(listImages[i]),
),
)
],
),
)
Below are the methods used to prepare the layout
// this method return the layout as per your expectations, here images are the // list of items you want to use and max count are the list of max item you want // to show except the count tile.
Widget getItems(List<String> images, int maxCount) =>
Stack(
children: List.generate(images.length <= maxCount ? images.length : maxCount+1, (index) {
if(index == maxCount){
return Positioned(
left: index * 60,
child: Container(
padding: const EdgeInsets.all(2), // Border width
decoration: BoxDecoration(
border: Border.all(
color: Colors.grey,
width: 2,
),
color: Colors.black,
borderRadius: BorderRadius.circular(40)),
child: SizedBox.fromSize(
size: const Size.fromRadius(48), // Image radius
child: Center(
child: Text(
style: const TextStyle(
color: Color(0xff58D56D),
fontSize: 30
),
"+${images.length-maxCount}"
),
),
),
),
);
}
else {
return Positioned(
left: index * 60, child: getItemWidget(images[index]));
}
}),
);
// pass the image url you want to show.
Widget getItemWidget(String imageUrl) => Stack(
children: [
Container(
padding: const EdgeInsets.all(2), // Border width
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(40)),
child: ClipRRect(
borderRadius: BorderRadius.circular(40),
child: SizedBox.fromSize(
size: Size.fromRadius(48), // Image radius
child: Image.network(
imageUrl,
fit: BoxFit.fill,
),
),
),
),
Positioned(
bottom: 0,
left: 0,
child: Container(
padding: const EdgeInsets.all(2), // Border width
decoration: const BoxDecoration(
color: Colors.black,
shape: BoxShape.circle,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(100),
child: SizedBox.fromSize(
size: Size.fromRadius(20), // Image radius
child: Image.network(
"https://play-lh.googleusercontent.com/STIZ_iftiehDCSynHXQaLqiL-F4kbZwasXOB2nae5pXTOpNKz8XSd7_VCF1Zgc3Z8Q",
fit: BoxFit.contain,
),
),
),
),
)
],
);
Below code is used to show the items
getItems(["https://cdn.pixabay.com/photo/2013/07/13/10/07/man-156584__340.png",
"https://static.vecteezy.com/system/resources/thumbnails/001/993/889/small/beautiful-latin-woman-avatar-character-icon-free-vector.jpg",
"https://static.toiimg.com/thumb/resizemode-4,msid-76729536,width-1200,height-900/76729536.jpg",
"https://www.nj.com/resizer/zovGSasCaR41h_yUGYHXbVTQW2A=/1280x0/smart/cloudfront-us-east-1.images.arcpublishing.com/advancelocal/SJGKVE5UNVESVCW7BBOHKQCZVE.jpg",
"https://i.kinja-img.com/gawker-media/image/upload/t_original/ijsi5fzb1nbkbhxa2gc1.png"], 3),
)
Output:
The point is using Stack and Positioned to positiond widgets.
(index to padding, maxPerson to set maximum widget to show)
https://dartpad.dev/ example
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final colors = const [
Colors.yellow,
Colors.green,
Colors.blue,
Colors.orange,
Colors.cyan,
Colors.brown,
];
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: PersonStacker(colors: colors),
),
);
}
}
class PersonStacker extends StatelessWidget {
final List<Color> colors;
final int maxPerson;
const PersonStacker({required this.colors, this.maxPerson = 3});
#override
Widget build(BuildContext context) {
return Stack(
children: [
...colors
.getRange(0, maxPerson)
.toList()
.asMap()
.map((index, color) => MapEntry(index, Person(index, color: color)))
.values
.toList(),
...colors.length > maxPerson
? [Person(maxPerson, plus: colors.length - maxPerson)]
: []
],
);
}
}
class Person extends StatelessWidget {
final int index;
final Color color;
final Color colorBorder;
final double size = 100;
final double offset = 30;
final int? plus;
const Person(this.index,
{this.plus, this.color = Colors.black, this.colorBorder = Colors.black});
Widget renderChild() => plus != null
? Center(
child: Text(
"+$plus",
style: const TextStyle(color: Colors.white, fontSize: 20),
),
)
: Container();
#override
Widget build(BuildContext context) {
return Positioned(
left: index * offset,
child: Container(
width: size,
height: size,
decoration: BoxDecoration(
color: color,
border: Border.all(width: 2, color: colorBorder),
borderRadius: BorderRadius.circular(size),
),
child: renderChild(),
),
);
}
}
Result

how to display input value from text field to other pages?

I'm trying to display the input value below onto another page that contains radio buttons . my aim is that every time i write or change a value in a textfield it gets updated and displays onto a different page with the radio options . The snippet of code below is for the textfield - i used riverpod to connect it to firestore:
class AddMealPage extends ConsumerWidget {
const AddMealPage({Key? key}) : super(key: key);
static const String route = "/addMeal";
#override
Widget build(BuildContext context, WidgetRef ref) {
final model = ref.read(addMealProvider);
return LoadingLayer(
child: Scaffold(
appBar: AppBar(
elevation: 0,
iconTheme: const IconThemeData(
color: Colors.black,
),
title: const Text(
"Create Meal",
style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
),
backgroundColor: Colors.white,
),
bottomNavigationBar: Padding(
padding: const EdgeInsets.fromLTRB(24, 0, 24, 24),
child: MaterialButton(
padding: const EdgeInsets.all(16),
color: Colors.black,
onPressed: model.enabled
? () async {
try {
await model.writeMeal();
Navigator.pop(context);
} catch (e) {
AppSnackbar(context).error(e);
}
}
: null,
child: const Text(
"Add meal",
style: TextStyle(color: Color.fromARGB(255, 247, 245, 245)),
),
),
),
body: SingleChildScrollView(
child: Container(
padding:
const EdgeInsets.symmetric(vertical: 60.0, horizontal: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
GestureDetector(
onTap: () async {
final picked = await ImagePicker()
.pickImage(source: ImageSource.gallery);
if (picked != null) {
model.file = File(picked.path);
}
},
child: Container(
margin: const EdgeInsets.only(),
width: MediaQuery.of(context).size.width,
height: 210.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
image: (model.mealUrl != null || model.file != null)
? DecorationImage(
image: model.file != null
? FileImage(model.file!)
: NetworkImage(model.mealUrl!)
as ImageProvider,
fit: BoxFit.cover,
)
: null),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (model.mealUrl == null && model.file == null)
const Expanded(
child: Center(
child: Icon(
Icons.photo,
),
),
),
const Material(
color: Colors.transparent,
child: Padding(
padding: EdgeInsets.all(8.0),
),
),
],
),
),
),
const SizedBox(
height: 10,
),
const Padding(
padding: EdgeInsets.only(left: 15.0),
child: Text(
"Meal Name",
style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500),
),
),
Container(
height: 50,
padding: const EdgeInsets.only(top: 10),
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.symmetric(horizontal: 20),
child: TextFormField(
textInputAction: TextInputAction.done,
initialValue: model.mealName,
maxLines: 1,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Colors.grey),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(5),
borderSide: const BorderSide(color: Colors.grey),
),
),
onChanged: (v) => model.mealName = v,
),
),
I want to display the input value i get from the textfield above in the format of a radioButton on a seperate page :
enum Variant { mealName }
class RadioOption extends StatefulWidget {
const RadioOption({Key? key}) : super(key: key);
#override
State<RadioOption> createState() => _RadioOptionState();
}
class _RadioOptionState extends State<RadioOption> {
Variant? _character = Variant.mealName;
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
RadioListTile<Variant>(
title: const Text('$mealName'),
value: Variant.MealName,
groupValue: _character,
onChanged: (Variant? value) {
setState(() {
_character = value;
});
},
),
I don't see the whole architecture of the app. But if you want to transfer model.mealName, you can make String value (mealName) in your RadioOption, and pass it through the constructor:
final mealName;
RadioOption({required this.mealName});
And then access it in _RadioOptionState by:
title: const Text(widget.mealName);
You can use TextEditingController and add this controller to your TextFormField which have controller properties but you can't use initialValue and controller both so avoid initialValue. To put value you can use controller.text = your_value;
You value will be filled in text field.
To get value from TextFormField Use var newValue = controller.text; and send this newValue to another page in constructor.

How to pass image from toggle button to next page in flutter?

I am new to flutter and i am trying out new things.my question is how to pass image from toggle button to next page in flutter?
This is the code for toggle button which have image and text in it,I want to pass selected image to next page
Container(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: ToggleButtons(
borderWidth: 1,
children:<Widget> [
Padding(padding: EdgeInsets.only(left:10),
child:
CustomIcon(
text:Text("TATA ACE",style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold,fontSize: 12),),
image: Image.asset("images/delivery.png",scale: 9,),
isSelected: isSelected[0],
bgColor:const Color(0x000000),
),),
Padding(padding: EdgeInsets.only(left:10),
child:
CustomIcon(
text: Text("TATA APE",style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold,fontSize: 12),),
image: Image.asset("images/delivery.png",scale: 9,),
isSelected: isSelected[1],
bgColor: const Color(0x000000),
),),
CustomItem class is also defined below
class CustomIcon extends StatefulWidget {
final Image image;
final Text text;
final bool isSelected;
final Color bgColor;
const CustomIcon(
{Key? key,
required this.text,
required this.image,
this.isSelected = false,
this.bgColor = Colors.black, value1})
: super(key: key);
#override
_CustomIconState createState() => _CustomIconState();
}
class _CustomIconState extends State<CustomIcon> {
#override
Widget build(BuildContext context) {
return Container(
width: 105,
height: 105,
decoration: BoxDecoration(
color: widget.isSelected
? Colors.black : Colors.grey,shape: BoxShape.rectangle,
borderRadius: const BorderRadius.all(
Radius.circular(100),
),
),
child: Center(
child: Container(
padding: EdgeInsets.all(15),
height: 105,
width: 105,
decoration: BoxDecoration(
color: widget.bgColor,
borderRadius: const BorderRadius.all(
Radius.circular(100),
),
),
child:Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
widget.image,
widget.text,
],
),
),
),
);
}
}
I need to pass this text and image to the next page or just image to next screen.

Navigate to another page from ListTile

My app got a expandable List with multiple entries. I want to navigate to different pages by clicking on the ListTiles on the lowest level. I know that there's the 'onTap' method but it requires a buildContext which I dont have the way I programmed it.
I got the code from the web and modified it just a little. Heres the Code:
import 'package:flutter/material.dart';
import '../screens/model_view_screen.dart';
import '../models/entry.dart';
class EntryItem extends StatelessWidget {
const EntryItem(this.entry);
final Entry entry;
Widget _buildTiles(Entry root) {
if (root.children.isEmpty) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
border: Border.all(
width: 2,
color: Color.fromRGBO(3, 120, 163, 1),
),
),
child: ListTile(
leading: Image.asset(root.imageUrl),
title: Text(root.title),
onTap: () {},
),
),
);
}
return Padding(
padding: const EdgeInsets.all(10.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
border: Border.all(
width: 3,
color: Color.fromRGBO(3, 120, 163, 1),
),
),
child: ExpansionTile(
key: PageStorageKey<Entry>(root),
leading: Image.asset(root.imageUrl),
title: Text(root.title, style: TextStyle(color: Colors.black)),
children: root.children.map<Widget>(_buildTiles).toList(),
),
),
);
}
#override
Widget build(BuildContext context) {
return _buildTiles(entry);
}
}
The routeName of the destination is given in a List:
class Entry{
final String title;
final String imageUrl;
final String routeName;
final List<Entry> children;
Entry(this.title, this.imageUrl, this.routeName, [this.children = const <Entry>[]]);
}
Thank you in advance.
You want to pass BuildContext to _buildTiles:
Widget _buildTiles(BuildContext context, Entry root) {
....
child: ListTile(
leading: Image.asset(root.imageUrl),
title: Text(root.title),
onTap: () => Navigator.of(context).pushNamed(root.routeName),
),
....
children: root.children.map((e) => _buildTiles(context, e)).toList(),
....
#override
Widget build(BuildContext context) {
return _buildTiles(context, entry);
}

Flutter - Button Group style and position

I am trying to create something like the attached image. I got this far ...
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(32),
topRight: Radius.circular(32),
),
),
child: ButtonTheme(
child: ButtonBar(
alignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () => print('hi'),
child: Text('Referals'),
color: Color(0xff2FBBF0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(15.0),
topLeft: Radius.circular(15.0)),
),
),
RaisedButton(
onPressed: () => print('hii'),
child: Text('Stats'),
color: Color(0xff2FBBF0),
),
RaisedButton(
onPressed: () => print('hiii'),
child: Text('Edit Profile'),
color: Color(0xff2FBBF0),
// color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(15.0),
topRight: Radius.circular(15.0)),
),
),
],
),
),
),
),
But I don't really feel like it will look like the image.
I would also like the button group to be at the top of the Container. Now they're in the absolute center. Just like they would be if wrapped in a Center widget.
Here's the complete code. I have just used Container and Row because I find it more suitable and easy to achieve without any headache. :P
If you want with RaisedButton, figure it out.
Source:
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
#override
_DemoState createState() => new _DemoState();
}
class _DemoState extends State<Demo> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("DEMO")),
body: Padding( // used padding just for demo purpose to separate from the appbar and the main content
padding: EdgeInsets.all(10),
child: Container(
alignment: Alignment.topCenter,
child: Container(
height: 60,
padding: EdgeInsets.all(3.5),
width: MediaQuery.of(context).size.width * 0.9,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.all(Radius.circular(15)),
),
child: Row(
children: <Widget>[
Expanded(
child: InkWell(
onTap: () {},
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(12),
topLeft: Radius.circular(12))),
child: Text("Referrals",
style: TextStyle(
color: Colors.blue,
fontSize: 17,
)),
))),
Expanded(
child: InkWell(
onTap: () {},
child: Container(
alignment: Alignment.center,
child: Text("Stats",
style: TextStyle(
color: Colors.white, fontSize: 17)),
))),
Padding(
padding: EdgeInsets.symmetric(vertical: 5),
child: Container(color: Colors.white, width: 2)),
Expanded(
child: InkWell(
onTap: () {},
child: Container(
alignment: Alignment.center,
child: Text("Edit Profile",
style: TextStyle(
color: Colors.white, fontSize: 17)),
)))
],
)),
)));
}
}
Output Screenshot:
Check my ButtonGroup widget that I created
import 'package:flutter/material.dart';
class ButtonGroup extends StatelessWidget {
static const double _radius = 10.0;
static const double _outerPadding = 2.0;
final int current;
final List<String> titles;
final ValueChanged<int> onTab;
final Color color;
final Color secondaryColor;
const ButtonGroup({
Key key,
this.titles,
this.onTab,
int current,
Color color,
Color secondaryColor,
}) : assert(titles != null),
current = current ?? 0,
color = color ?? Colors.blue,
secondaryColor = secondaryColor ?? Colors.white,
super(key: key);
#override
Widget build(BuildContext context) {
return Material(
color: color,
borderRadius: BorderRadius.circular(_radius),
child: Padding(
padding: const EdgeInsets.all(_outerPadding),
child: ClipRRect(
borderRadius: BorderRadius.circular(_radius - _outerPadding),
child: IntrinsicHeight(
child: Row(
mainAxisSize: MainAxisSize.min,
children: _buttonList(),
),
),
),
),
);
}
List<Widget> _buttonList() {
final buttons = <Widget>[];
for (int i = 0; i < titles.length; i++) {
buttons.add(_button(titles[i], i));
buttons.add(
VerticalDivider(
width: 1.0,
color: (i == current || i + 1 == current) ? color : secondaryColor,
thickness: 1.5,
indent: 5.0,
endIndent: 5.0,
),
);
}
buttons.removeLast();
return buttons;
}
Widget _button(String title, int index) {
if (index == this.current)
return _activeButton(title);
else
return _inActiveButton(title, index);
}
Widget _activeButton(String title) => FlatButton(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
disabledColor: secondaryColor,
disabledTextColor: color,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.zero,
),
child: Text(title),
onPressed: null,
);
Widget _inActiveButton(String title, int index) => FlatButton(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
color: Colors.transparent,
textColor: Colors.white,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.zero,
),
child: Text(title),
onPressed: () {
if (onTab != null) onTab(index);
},
);
}
You can use it like this
ButtonGroup(
titles: ["Button1", "Button2", "Button3"],
current: index,
color: Colors.blue,
secondaryColor: Colors.white,
onTab: (selected) {
setState(() {
index = selected;
});
},
)
Example:
import 'package:flutter/material.dart';
import 'package:flutter_app_test2/btn_grp.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
int current = 0;
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ButtonGroup(
titles: ["Button1", "Button2", "Button3", "Button3"],
current: current,
onTab: (selected) {
print(selected);
setState(() {
current = selected;
});
},
),
),
);
}
}
try adding following in all RaisedButton widgets:
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
and buttonPadding: EdgeInsets.all(1), in ButtonBar
Source: https://api.flutter.dev/flutter/material/MaterialTapTargetSize-class.html