可以在列表磁贴下方添加其他小部件。

huangapple go评论63阅读模式
英文:

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
        {
            &quot;id&quot;: &quot;p1&quot;,
            &quot;name&quot;: &quot;Item 1&quot;,
            &quot;phone_number&quot;:&quot;8975412369&quot;,
            &quot;email_id&quot;:&quot;abc@gmail.com&quot;,
            &quot;description&quot;: &quot;Description 1&quot;,
            &quot;imageUrl&quot;: &quot;https://m.media-amazon.com/images/I/81S-ekaE+vS._AC_UL320_.jpg&quot;
        },
*/
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&lt;String, dynamic&gt; json) =&gt; Item(
        id: json[&quot;id&quot;],
        name: json[&quot;name&quot;],
        phoneNumber: json[&quot;phone_number&quot;],
        emailId: json[&quot;email_id&quot;],
        description: json[&quot;description&quot;],
        imageUrl: json[&quot;imageUrl&quot;],
      );

  Map&lt;String, dynamic&gt; toJson() =&gt; {
        &quot;id&quot;: id,
        &quot;name&quot;: name,
        &quot;phone_number&quot;: phoneNumber,
        &quot;email_id&quot;: emailId,
        &quot;description&quot;: description,
        &quot;imageUrl&quot;: imageUrl,
      };

  static List&lt;Item&gt; fromJsonList(List&lt;Map&lt;String,dynamic&gt;&gt; jsonList) {
    final List&lt;Item&gt; 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 &#39;package:flutter/material.dart&#39;;

import &#39;data_model.dart&#39;;

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: () =&gt; 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(
          &#39;Ask Help&#39;,
          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: &lt;Widget&gt;[
                      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(&#39;Patient&#39;),
                      ),
                      OutlinedButton(
                        style: OutlinedButton.styleFrom(
                            side:
                                const BorderSide(color: Colors.teal, width: 1),
                            minimumSize: const Size(160, 50)),
                        onPressed: () {},
                        child: const Text(
                          &quot;NGO&quot;,
                          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 [&#39;plumber&#39;, &#39;2 hrs&#39;]),
                ),
                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(&#39;Register&#39;),
                      ),
                    ),
                  ),
                )
              ],
            ),
          ),
        );
      }),
    );
  }
}

class DetailSection extends StatelessWidget {
  final Item item;
  final List&lt;String&gt; 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(
              &#39;Skills&#39;,
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
          ),
          const SizedBox(
            height: 15,
          ),
          Row(
            children: skills
                .map((e) =&gt; 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(
            &#39;Needs Help for&#39;,
            style: TextStyle(fontWeight: FontWeight.bold),
          ),
        ),
        const SizedBox(
          height: 10,
        ),
        const Align(
            alignment: Alignment.centerLeft,
            child: Text(&#39;Bluh bluh aaaaaaah I need help!&#39;)),
        const SizedBox(
          height: 10,
        ),
        if (viewAttachment)
          Row(
            children: const [
              Text(
                &#39;View Attachment&#39;,
                style: TextStyle(fontWeight: FontWeight.bold),
              ),
              Spacer(),
              Icon(Icons.play_arrow_outlined),
            ],
          ),
      ],
    );
  }
}

Edited main.dart:

import &#39;dart:convert&#39;;

import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter/services.dart&#39;;
import &#39;details.dart&#39;;

import &#39;data_model.dart&#39;;

void main() =&gt; 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&lt;HomePage&gt; createState() =&gt; _HomePageState();
}

class _HomePageState extends State&lt;HomePage&gt; {
  final List&lt;Item&gt; _items =
      []; // Don&#39;t use dynamic types. List&lt;T&gt; where T is your type is the better form :)

  // Fetch content from the json file
  Future&lt;void&gt; readJson() async {
    final String response = await rootBundle.loadString(&#39;assets/sample.json&#39;);
    // jsonDecode/json.decode are not futures. No need to &quot;await&quot; them.
    final rawItems = json.decode(response)[&#39;items&#39;];
    final jsonList = rawItems.cast&lt;Map&lt;String, dynamic&gt;&gt;().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(
          &#39;Item List&#39;,
          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) =&gt;
                      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: &lt;Widget&gt;[
                              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: () =&gt;
                            Navigator.of(context).push(MaterialPageRoute(
                                builder: ((context) =&gt; 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.

huangapple
  • 本文由 发表于 2023年4月20日 02:14:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/76057664.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定