英文:
Can I add other widget below list tile
问题
我试图实现这个:
但由于我对此不了解如何实现。我查了很多代码,但没有帮到我,所以请帮助我。我是编程的初学者。
在此代码中,我使用了 JSON 文件,并且我能够显示列表项,但如图所示,我想在此列表项下方添加一些文本和图标,但当我添加任何小部件时,它会移到导航按钮的旁边。
我不明白该如何做到这一点。
英文:
I am trying to achieve this:
But as I m new to this don't understand how to implement. I check many code but nothing help me so please help me. I am beginner in the coding.
In this code I use json file and I am able to display list tile but as shown in the image I want to add some text and icon below this list tile but when I add any widget it goes side to leading
I don't understand how do I do this
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List _items = [];
// Fetch content from the json file
Future<void> readJson() async {
final String response = await rootBundle.loadString('assets/sample.json');
final data = await json.decode(response);
setState(() {
_items = data["items"];
});
}
@override
void initState() {
super.initState();
// Call the readJson method when the app starts
readJson();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.black),
onPressed: () => Navigator.of(context).pop(),
),
backgroundColor: Colors.white,
centerTitle: true,
elevation: 0,
title: const Text(
'Ask Help',
style: TextStyle(color: Colors.black),
),
),
body: Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 12, 0),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
children: <Widget>[
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
//change width and height on your need width = 200 and height = 50
minimumSize: Size(160, 50),
),
child: const Text('Patient'),
)
],
),
Column(
children: <Widget>[
OutlinedButton(
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Colors.blue, width: 1),
minimumSize: Size(160, 50)),
onPressed: () {},
child: const Text(
"NGO",
style: TextStyle(color: Colors.blue),
),
),
],
)
],
),
// Display the data loaded from sample.json
Container(
child: _items.isNotEmpty
? Expanded(
child: ListView.separated(
itemCount: _items.length,
separatorBuilder: (BuildContext context, int index) =>
Divider(height: 1),
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
leading: CircleAvatar(
child:
Text(_items[index]["imageUrl"].toString()),
),
title: Text(_items[index]["name"]),
subtitle: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(_items[index]["phone_number"],
style: const TextStyle(
fontSize: 13.0,
fontWeight: FontWeight.normal)),
Text(_items[index]["email_id"],
style: const TextStyle(
fontSize: 13.0,
fontWeight: FontWeight.normal)),
// Text(
// 'Population: ${_items[index]["email_id"]}',
// style: const TextStyle(
// fontSize: 11.0,
// fontWeight: FontWeight.normal),
// ),
]
),
trailing: const Icon(Icons.more_vert),
),
);
},
),
)
: Container(
//color: Colors.amber,
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
height: 70,
child: Center(
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
//change width and height on your need width = 200 and height = 50
minimumSize: Size(300, 50),
),
child: const Text('Register'),
),
),
),
)
],
),
),
);
}
}
My json code:
{
"items": [
{
"id": "p1",
"name": "Item 1",
"phone_number":"8975412369",
"email_id":"abc@gmail.com",
"description": "Description 1",
"imageUrl": "https://m.media-amazon.com/images/I/81S-ekaE+vS._AC_UL320_.jpg"
},
{
"id": "p2",
"name": "Item 2",
"phone_number": "8975412369",
"email_id": "abc@gmail.com",
"description": "Description 1",
"imageUrl": "https://m.media-amazon.com/images/I/81S-ekaE+vS._AC_UL320_.jpg"
},
{
"id": "p3",
"name": "Item 3",
"phone_number": "8975412369",
"email_id": "abc@gmail.com",
"description": "Description 1",
"imageUrl": "https://m.media-amazon.com/images/I/81S-ekaE+vS._AC_UL320_.jpg"
},
{
"id": "p1",
"name": "Item 1",
"phone_number": "8975412369",
"email_id": "abc@gmail.com",
"description": "Description 1",
"imageUrl": "https://m.media-amazon.com/images/I/81S-ekaE+vS._AC_UL320_.jpg"
},
{
"id": "p2",
"name": "Item 2",
"phone_number": "8975412369",
"email_id": "abc@gmail.com",
"description": "Description 1",
"imageUrl": "https://m.media-amazon.com/images/I/81S-ekaE+vS._AC_UL320_.jpg"
},
{
"id": "p3",
"name": "Item 3",
"phone_number": "8975412369",
"email_id": "abc@gmail.com",
"description": "Description 1",
"imageUrl": "https://m.media-amazon.com/images/I/81S-ekaE+vS._AC_UL320_.jpg"
},
{
"id": "p1",
"name": "Item 1",
"phone_number": "8975412369",
"email_id": "abc@gmail.com",
"description": "Description 1",
"imageUrl": "https://m.media-amazon.com/images/I/81S-ekaE+vS._AC_UL320_.jpg"
},
{
"id": "p2",
"name": "Item 2",
"phone_number": "8975412369",
"email_id": "abc@gmail.com",
"description": "Description 1",
"imageUrl": "https://m.media-amazon.com/images/I/81S-ekaE+vS._AC_UL320_.jpg"
},
{
"id": "p3",
"name": "Item 3",
"phone_number": "8975412369",
"email_id": "abc@gmail.com",
"description": "Description 1",
"imageUrl": "https://m.media-amazon.com/images/I/81S-ekaE+vS._AC_UL320_.jpg"
},{
"id": "p1",
"name": "Item 1",
"phone_number": "8975412369",
"email_id": "abc@gmail.com",
"description": "Description 1",
"imageUrl": "https://m.media-amazon.com/images/I/81S-ekaE+vS._AC_UL320_.jpg"
},
{
"id": "p2",
"name": "Item 2",
"phone_number": "8975412369",
"email_id": "abc@gmail.com",
"description": "Description 1",
"imageUrl": "https://m.media-amazon.com/images/I/81S-ekaE+vS._AC_UL320_.jpg"
},
{
"id": "p3",
"name": "Item 3",
"phone_number": "8975412369",
"email_id": "abc@gmail.com",
"description": "Description 1",
"imageUrl": "https://m.media-amazon.com/images/I/81S-ekaE+vS._AC_UL320_.jpg"
}
]
}
答案1
得分: 1
class Item {
Item({
required this.id,
required this.name,
required this.phoneNumber,
required this.emailId,
required this.description,
required this.imageUrl,
});
final String id;
final String name;
final String phoneNumber;
final String emailId;
final String description;
final String imageUrl;
factory Item.fromJson(Map<String, dynamic> json) => Item(
id: json["id"],
name: json["name"],
phoneNumber: json["phone_number"],
emailId: json["email_id"],
description: json["description"],
imageUrl: json["imageUrl"],
);
Map<String, dynamic> toJson() => {
"id": id,
"name": name,
"phone_number": phoneNumber,
"email_id": emailId,
"description": description,
"imageUrl": imageUrl,
};
static List<Item> fromJsonList(List<Map<String, dynamic>> jsonList) {
final List<Item> items = [];
for (var json in jsonList) {
items.add(Item.fromJson(json));
}
return items;
}
}
class DetailsPage extends StatelessWidget {
const DetailsPage({Key? key, required this.item}) : super(key: key);
final Item item;
// ... (rest of the DetailsPage code remains unchanged)
}
class DetailSection extends StatelessWidget {
final Item item;
final List<String> skills;
final bool viewAttachment;
const DetailSection(
{Key? key,
required this.item,
required this.viewAttachment,
this.skills = const []})
: super(key: key);
// ... (rest of the DetailSection code remains unchanged)
}
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final List<Item> _items = [];
// ... (rest of the _HomePageState code remains unchanged)
}
英文:
The answer to the question is yes!
Here's a minimal example:
/* SAMPLE
{
"id": "p1",
"name": "Item 1",
"phone_number":"8975412369",
"email_id":"abc@gmail.com",
"description": "Description 1",
"imageUrl": "https://m.media-amazon.com/images/I/81S-ekaE+vS._AC_UL320_.jpg"
},
*/
class Item {
Item({
required this.id,
required this.name,
required this.phoneNumber,
required this.emailId,
required this.description,
required this.imageUrl,
});
final String id;
final String name;
final String phoneNumber;
final String emailId;
final String description;
final String imageUrl;
factory Item.fromJson(Map<String, dynamic> json) => Item(
id: json["id"],
name: json["name"],
phoneNumber: json["phone_number"],
emailId: json["email_id"],
description: json["description"],
imageUrl: json["imageUrl"],
);
Map<String, dynamic> toJson() => {
"id": id,
"name": name,
"phone_number": phoneNumber,
"email_id": emailId,
"description": description,
"imageUrl": imageUrl,
};
static List<Item> fromJsonList(List<Map<String,dynamic>> jsonList) {
final List<Item> items = [];
for (var json in jsonList) {
items.add(Item.fromJson(json));
}
return items;
}
}
The above class goes in data_model.dart
file.
I have made a data model for you. Before working on UI, it's best to organize your code into different parts.
There is also this custom widget made for easier handling of stuff and the DRY rule!
This goes to the details.dart
file.
import 'package:flutter/material.dart';
import 'data_model.dart';
class DetailsPage extends StatelessWidget {
const DetailsPage({super.key, required this.item});
final Item item;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.black),
onPressed: () => Navigator.of(context)
.pop(), // No need to pop for now, there is nothing behind this screen :)
),
backgroundColor: Colors.white,
centerTitle: true,
elevation: 0,
title: const Text(
'Ask Help',
style: TextStyle(color: Colors.black),
),
),
body: LayoutBuilder(builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: constraints,
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.teal.withBlue(200),
//change width and height on your need width = 200 and height = 50
minimumSize: const Size(160, 50),
),
child: const Text('Patient'),
),
OutlinedButton(
style: OutlinedButton.styleFrom(
side:
const BorderSide(color: Colors.teal, width: 1),
minimumSize: const Size(160, 50)),
onPressed: () {},
child: const Text(
"NGO",
style: TextStyle(color: Colors.blue),
),
),
],
),
),
const SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: DetailSection(item: item, viewAttachment: true),
),
const SizedBox(
height: 10,
),
const Divider(
color: Colors.black26,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: DetailSection(
item: item,
viewAttachment: false,
skills: const ['plumber', '2 hrs']),
),
const Divider(
color: Colors.black26,
),
const Spacer(),
Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
height: 70,
child: Center(
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.teal.withBlue(200),
minimumSize: const Size(300, 50),
),
child: const Text('Register'),
),
),
),
)
],
),
),
);
}),
);
}
}
class DetailSection extends StatelessWidget {
final Item item;
final List<String> skills;
final bool viewAttachment;
const DetailSection(
{Key? key,
required this.item,
required this.viewAttachment,
this.skills = const []})
: super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: [
ListTile(
leading: CircleAvatar(
radius: 35,
backgroundColor: Colors.transparent,
child: Image.network(item.imageUrl),
),
title: Text(
item.name,
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(item.phoneNumber,
style: const TextStyle(
fontSize: 13.0, fontWeight: FontWeight.normal)),
Text(item.emailId,
style: const TextStyle(
fontSize: 13.0, fontWeight: FontWeight.normal)),
],
),
),
const SizedBox(
height: 10,
),
if (skills.isNotEmpty) ...[
const Align(
alignment: Alignment.centerLeft,
child: Text(
'Skills',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
const SizedBox(
height: 15,
),
Row(
children: skills
.map((e) => Container(
margin: const EdgeInsets.only(
right: 10,
bottom: 10,
),
padding: const EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.amberAccent.withOpacity(0.5),
borderRadius:
const BorderRadius.all(Radius.circular(12))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 6.0),
child: Text(e),
)))
.toList(),
)
],
const Align(
alignment: Alignment.centerLeft,
child: Text(
'Needs Help for',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
const SizedBox(
height: 10,
),
const Align(
alignment: Alignment.centerLeft,
child: Text('Bluh bluh aaaaaaah I need help!')),
const SizedBox(
height: 10,
),
if (viewAttachment)
Row(
children: const [
Text(
'View Attachment',
style: TextStyle(fontWeight: FontWeight.bold),
),
Spacer(),
Icon(Icons.play_arrow_outlined),
],
),
],
);
}
}
Edited main.dart:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'details.dart';
import 'data_model.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final List<Item> _items =
[]; // Don't use dynamic types. List<T> where T is your type is the better form :)
// Fetch content from the json file
Future<void> readJson() async {
final String response = await rootBundle.loadString('assets/sample.json');
// jsonDecode/json.decode are not futures. No need to "await" them.
final rawItems = json.decode(response)['items'];
final jsonList = rawItems.cast<Map<String, dynamic>>().toList();
final items = Item.fromJsonList(jsonList);
setState(() {
_items.addAll(items);
});
}
@override
void initState() {
super.initState();
// Call the readJson method when the app starts
readJson();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
centerTitle: true,
elevation: 0,
title: const Text(
'Item List',
style: TextStyle(color: Colors.black),
),
),
body: Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 12, 0),
child: Container(
child: _items.isNotEmpty
? ListView.separated(
itemCount: _items.length,
separatorBuilder: (BuildContext context, int index) =>
const Divider(height: 1),
itemBuilder: (context, index) {
final item = _items[
index]; // No need to use [index] for each item :)
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
leading: CircleAvatar(
child: Image.network(item
.imageUrl), // Optionally, use cached_network_image package.
),
title: Text(item.name),
subtitle: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(item.phoneNumber,
style: const TextStyle(
fontSize: 13.0,
fontWeight: FontWeight.normal)),
Text(item.emailId,
style: const TextStyle(
fontSize: 13.0,
fontWeight: FontWeight.normal)),
]),
trailing: const Icon(Icons.more_vert),
onTap: () =>
Navigator.of(context).push(MaterialPageRoute(
builder: ((context) => DetailsPage(
item: item,
)))),
),
);
},
)
: const CircularProgressIndicator(),
),
),
);
}
}
I have modified the UI code to represent the model changes.
Your code and JSON sample are a bit far from the linked image you want to implement, but I have used made-up details to represent a better example to you. Feel free to play with the code and UI.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论