显示单页中API的嵌套JSON,带有多个列表

发布于 2025-01-21 09:07:54 字数 7810 浏览 0 评论 0原文

我是新手的Flutter,我正在为API的嵌套JSON挣扎,我想在一个页面中显示该数据。

我从URL中获得此JSON并在类中解码,这很好:

{
  "service1": [
    {
      "firstname": "Peter",
      "lastname": "Smith"
    },
    {
      "firstname": "Paul",
      "lastname": "Johnson"
    }
  ],
  "service2": [
    {
      "firstname": "Mary",
      "lastname": "Williams"
    },
    {
      "firstname": "Guy",
      "lastname": "Brown"
    }
  ]
}

类:

/*------------------------------
staff.dart
------------------------------*/
import 'dart:convert';

class Staff {
  String? service;
  String? firstname;
  String? lastname;

  Staff({this.service, this.firstname, this.lastname});

  factory Staff.fromMap(Map<String, dynamic> data) => Staff(
        service: data['service'] as String?,
        firstname: data['firstname'] as String?,
        lastname: data['lastname'] as String?,
      );

  Map<String, dynamic> toMap() => {
        'service': '',
        'firstname': firstname,
        'lastname': lastname,
      };

  /// Parses the string and returns the resulting Json object.
  factory Staff.fromJson(String data) {
    return Staff.fromMap(json.decode(data) as Map<String, dynamic>);
  }

  /// Converts [Staff] to a JSON string.
  String toJson() => json.encode(toMap());
}


/*------------------------------
servicedesk.dart
------------------------------*/
import 'dart:convert';
import 'staff.dart';

class ServiceDesk {
  List<Staff>? service1;
  List<Staff>? service2;

  ServiceDesk({
    this.service1,
    this.service2,
  });

  factory ServiceDesk.fromMap(Map<String, dynamic> data) => ServiceDesk(
        service1: (data['service1'] as List<dynamic>?)
            ?.map((e) => Staff.fromMap(e as Map<String, dynamic>))
            .toList(),
        service2: (data['service2'] as List<dynamic>?)
            ?.map((e) => Staff.fromMap(e as Map<String, dynamic>))
            .toList(),
      );

  Map<String, dynamic> toMap() => {
        'service1': service1?.map((e) => e.toMap()).toList(),
        'service2': service2?.map((e) => e.toMap()).toList(),
      };

  /// Parses the string and returns the resulting Json object as [ServiceDesk].
  factory ServiceDesk.fromJson(String data) {
    var object = ServiceDesk.fromMap(json.decode(data) as Map<String, dynamic>);
    object.b1!.insert(0, Staff(service: 'Title for Service1'));
    object.b2!.insert(0, Staff(service: 'Title for Service2'));
    return object;
  }

  /// Converts to a JSON string.
  String toJson() => json.encode(toMap());
}

这是我拥有的代码(伪码):

  // PSEUDOCODE!!
  Widget ListWithService(List<Staff>? entry) {
    return ListView.builder(
        itemCount: entry!.length,
        padding: const EdgeInsets.all(2.0),
        itemBuilder: (context, position) {
          final item = entry[position];
          if (item.service != null) {
            return ListTile(
              title: Text(
                '${item.service}',
                style: Theme.of(context).textTheme.headline5,
              ),
            );
          } else {
            return ListTile(
              title: Text(
                '${item.firstname} ${item.lastname}',
                style: Theme.of(context).textTheme.bodyText1,
              ),
            );
          }
        });
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Service Desk'),
      ),
      body: FutureBuilder<sdclass.ServiceDesk>(
        future: getData(),
        builder: (context, snapshot) {
          if (snapshot.hasData == true) {
            return [
              ListWithService(snapshot.data.service1), 
              ListWithSercice(snapshot.data.service1);
            ] // PSEUDOCODE!!
          } else if (snapshot.hasError) {
            return const Icon(
              Icons.error_outline,
              color: Colors.red,
              size: 60,
            );
          } else {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }
        }
      ),
    );
  }

我最终应该在完整页面上看起来像这样:

Service 1 (标题)
彼得·史密斯
保罗·约翰逊(Paul Johnson)

Service 2 (标题)
玛丽·威廉姆斯
盖伊·布朗(Guy Brown)

有人可以帮我处理代码以使其工作吗?

更新代码

感谢您的更新示例。我用代码测试了它。首先,一切看起来都很好。但是Wenn我使用JSON切换到屏幕,我会发现一个错误:

期望'futureor的值

    import 'dart:convert';
    import 'dart:core';
    
    import 'package:flutter/material.dart';
    import 'package:http/http.dart' as http;
    
    class ServiceDesk extends StatelessWidget {
      const ServiceDesk({Key? key}) : super(key: key);
    
      Future<Map<String, List<Map<String, String>>>> getData() async {
        String link = "https://url-to-json-file.json";
        final res = await http
            .get(Uri.parse(link), headers: {"Accept": "application/json"});
        if (res.statusCode == 200) {
          var utf8decoded = utf8.decode(res.body.toString().codeUnits);
          var decoded = json.decode(utf8decoded);
          return decoded;
        } else {
          throw Exception('Failed to load JSON');
        }
      }
    
      Widget listViewWidget(
          Iterable<MapEntry<String, List<Map<String, String>>>> entries) {
        return ListView.builder(
            itemCount: entries.length,
            padding: const EdgeInsets.all(2.0),
            itemBuilder: (context, index) {
              final entry = entries.elementAt(index);
              final key = entry.key;
              final values = entry.value;
    
              return Column(
                children: [
                  ListTile(
                    title: Text(
                      'Title for $key',
                      style: Theme.of(context).textTheme.headline5,
                    ),
                  ),
                  for (var person in values)
                    ListTile(
                      title: Text(
                        '${person["firstname"]} ${person["lastname"]}',
                        style: Theme.of(context).textTheme.bodyText1,
                      ),
                    ),
                ],
              );
            });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Service Desk'),
          ),
          body: FutureBuilder(
              future: getData(),
              builder: (_,
                  AsyncSnapshot<Map<String, List<Map<String, String>>>> snapshot) {
                if (snapshot.hasData == true) {
                  final entries = snapshot.data?.entries ?? {};
    
                  return listViewWidget(entries);
                } else if (snapshot.hasError) {
                  return Center(
                      child: Column(
                    mainAxisSize: MainAxisSize.min,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      const Icon(
                        Icons.error_outline,
                        color: Colors.red,
                        size: 60,
                      ),
                      Text("Fehler: ${snapshot.error}"),
                    ],
                  ));
                } else {
                  return const Center(
                    child: CircularProgressIndicator(),
                  );
                }
              }),
        );
      }
    }

Im new in Flutter and i'm struggeling with a nested JSON from API which data i want to show in one single page.

I get this JSON from a URL and decode it in a class, which is working fine:

{
  "service1": [
    {
      "firstname": "Peter",
      "lastname": "Smith"
    },
    {
      "firstname": "Paul",
      "lastname": "Johnson"
    }
  ],
  "service2": [
    {
      "firstname": "Mary",
      "lastname": "Williams"
    },
    {
      "firstname": "Guy",
      "lastname": "Brown"
    }
  ]
}

Classes:

/*------------------------------
staff.dart
------------------------------*/
import 'dart:convert';

class Staff {
  String? service;
  String? firstname;
  String? lastname;

  Staff({this.service, this.firstname, this.lastname});

  factory Staff.fromMap(Map<String, dynamic> data) => Staff(
        service: data['service'] as String?,
        firstname: data['firstname'] as String?,
        lastname: data['lastname'] as String?,
      );

  Map<String, dynamic> toMap() => {
        'service': '',
        'firstname': firstname,
        'lastname': lastname,
      };

  /// Parses the string and returns the resulting Json object.
  factory Staff.fromJson(String data) {
    return Staff.fromMap(json.decode(data) as Map<String, dynamic>);
  }

  /// Converts [Staff] to a JSON string.
  String toJson() => json.encode(toMap());
}


/*------------------------------
servicedesk.dart
------------------------------*/
import 'dart:convert';
import 'staff.dart';

class ServiceDesk {
  List<Staff>? service1;
  List<Staff>? service2;

  ServiceDesk({
    this.service1,
    this.service2,
  });

  factory ServiceDesk.fromMap(Map<String, dynamic> data) => ServiceDesk(
        service1: (data['service1'] as List<dynamic>?)
            ?.map((e) => Staff.fromMap(e as Map<String, dynamic>))
            .toList(),
        service2: (data['service2'] as List<dynamic>?)
            ?.map((e) => Staff.fromMap(e as Map<String, dynamic>))
            .toList(),
      );

  Map<String, dynamic> toMap() => {
        'service1': service1?.map((e) => e.toMap()).toList(),
        'service2': service2?.map((e) => e.toMap()).toList(),
      };

  /// Parses the string and returns the resulting Json object as [ServiceDesk].
  factory ServiceDesk.fromJson(String data) {
    var object = ServiceDesk.fromMap(json.decode(data) as Map<String, dynamic>);
    object.b1!.insert(0, Staff(service: 'Title for Service1'));
    object.b2!.insert(0, Staff(service: 'Title for Service2'));
    return object;
  }

  /// Converts to a JSON string.
  String toJson() => json.encode(toMap());
}

That's the code i have (Pseudocode between):

  // PSEUDOCODE!!
  Widget ListWithService(List<Staff>? entry) {
    return ListView.builder(
        itemCount: entry!.length,
        padding: const EdgeInsets.all(2.0),
        itemBuilder: (context, position) {
          final item = entry[position];
          if (item.service != null) {
            return ListTile(
              title: Text(
                '${item.service}',
                style: Theme.of(context).textTheme.headline5,
              ),
            );
          } else {
            return ListTile(
              title: Text(
                '${item.firstname} ${item.lastname}',
                style: Theme.of(context).textTheme.bodyText1,
              ),
            );
          }
        });
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Service Desk'),
      ),
      body: FutureBuilder<sdclass.ServiceDesk>(
        future: getData(),
        builder: (context, snapshot) {
          if (snapshot.hasData == true) {
            return [
              ListWithService(snapshot.data.service1), 
              ListWithSercice(snapshot.data.service1);
            ] // PSEUDOCODE!!
          } else if (snapshot.hasError) {
            return const Icon(
              Icons.error_outline,
              color: Colors.red,
              size: 60,
            );
          } else {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }
        }
      ),
    );
  }

What i would have at the end should look like this on the full page:

Title for Service1 (Headline)
Peter Smith
Paul Johnson

Title for Service2 (Headline)
Mary Williams
Guy Brown

Could someone help me with the code to get it work?

Update Code

Thanks for your updated example. I tested it in my code. First, everything looks fine. But wenn i switch to the screen with the json, i get a error:

Expected a value of type 'FutureOr<Map<String, List<Map<String, String>>>>', but got one of type '_JsonMap'

    import 'dart:convert';
    import 'dart:core';
    
    import 'package:flutter/material.dart';
    import 'package:http/http.dart' as http;
    
    class ServiceDesk extends StatelessWidget {
      const ServiceDesk({Key? key}) : super(key: key);
    
      Future<Map<String, List<Map<String, String>>>> getData() async {
        String link = "https://url-to-json-file.json";
        final res = await http
            .get(Uri.parse(link), headers: {"Accept": "application/json"});
        if (res.statusCode == 200) {
          var utf8decoded = utf8.decode(res.body.toString().codeUnits);
          var decoded = json.decode(utf8decoded);
          return decoded;
        } else {
          throw Exception('Failed to load JSON');
        }
      }
    
      Widget listViewWidget(
          Iterable<MapEntry<String, List<Map<String, String>>>> entries) {
        return ListView.builder(
            itemCount: entries.length,
            padding: const EdgeInsets.all(2.0),
            itemBuilder: (context, index) {
              final entry = entries.elementAt(index);
              final key = entry.key;
              final values = entry.value;
    
              return Column(
                children: [
                  ListTile(
                    title: Text(
                      'Title for $key',
                      style: Theme.of(context).textTheme.headline5,
                    ),
                  ),
                  for (var person in values)
                    ListTile(
                      title: Text(
                        '${person["firstname"]} ${person["lastname"]}',
                        style: Theme.of(context).textTheme.bodyText1,
                      ),
                    ),
                ],
              );
            });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Service Desk'),
          ),
          body: FutureBuilder(
              future: getData(),
              builder: (_,
                  AsyncSnapshot<Map<String, List<Map<String, String>>>> snapshot) {
                if (snapshot.hasData == true) {
                  final entries = snapshot.data?.entries ?? {};
    
                  return listViewWidget(entries);
                } else if (snapshot.hasError) {
                  return Center(
                      child: Column(
                    mainAxisSize: MainAxisSize.min,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      const Icon(
                        Icons.error_outline,
                        color: Colors.red,
                        size: 60,
                      ),
                      Text("Fehler: ${snapshot.error}"),
                    ],
                  ));
                } else {
                  return const Center(
                    child: CircularProgressIndicator(),
                  );
                }
              }),
        );
      }
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

迟到的我 2025-01-28 09:07:54

在DART语言中,您可以在列表中使用循环,它使使用Flutter UI更容易。

import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'dart:convert';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const RootView(),
    );
  }
}

class RootView extends StatelessWidget {
  const RootView({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: TextButton(
          child: const Text('GO TO SAFF SERVICE'),
          onPressed: () {
            Navigator.push(context, Home.route());
          },
        ),
      ),
    );
  }
}

class Home extends StatefulWidget {
  const Home({Key? key}) : super(key: key);

  static Route route() {
    return CupertinoPageRoute(builder: (_) => const Home());
  }

  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  late Future<StaffService> staffService;

  @override
  void initState() {
    staffService = getStaffService();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Services')),
      body: Center(
        child: FutureBuilder<StaffService>(
          future: staffService,
          builder: (_, snapshot) {
            if (snapshot.hasData) {
              final saff = snapshot.data!;

              return ListView(
                children: [
                  if (saff.service1.isNotEmpty)
                    StaffServiceTile(
                      title: Text(
                        'Title for Service 1',
                        style: Theme.of(context).textTheme.headline5,
                      ),
                      services: saff.service1,
                    ),
                  if (saff.service2.isNotEmpty)
                    StaffServiceTile(
                      title: Text(
                        'Title for Service 2',
                        style: Theme.of(context).textTheme.headline5,
                      ),
                      services: saff.service2,
                    ),
                ],
              );
            } else if (snapshot.hasError) {
              return const Text('Error on loaad data. Try again later.');
            } else {
              return const CircularProgressIndicator();
            }
          },
        ),
      ),
    );
  }
}

class StaffServiceTile extends StatelessWidget {
  const StaffServiceTile({
    Key? key,
    required this.title,
    required this.services,
  }) : super(key: key);

  final Widget title;
  final List<Service> services;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ListTile(title: title),
        for (var person in services)
          ListTile(
            title: Text(
              '${person.firstname} ${person.lastname}',
            ),
          ),
      ],
    );
  }
}

class StaffService {
  StaffService({this.service1 = const [], this.service2 = const []});

  List<Service> service1, service2;

  factory StaffService.fromJson(String str) {
    return StaffService.fromMap(json.decode(str));
  }

  String toJson() => json.encode(toMap());

  factory StaffService.fromMap(Map<String, dynamic> json) => StaffService(
        service1: List<Service>.from(json["service1"].map((x) => Service.fromMap(x))),
        service2: List<Service>.from(json["service2"].map((x) => Service.fromMap(x))),
      );

  Map<String, dynamic> toMap() => {
        "service1": List<dynamic>.from(service1.map((x) => x.toMap())),
        "service2": List<dynamic>.from(service2.map((x) => x.toMap())),
      };
}

class Service {
  Service({this.firstname, this.lastname});

  String? firstname, lastname;

  factory Service.fromJson(String str) => Service.fromMap(json.decode(str));

  String toJson() => json.encode(toMap());

  factory Service.fromMap(Map<String, dynamic> json) => Service(
        firstname: json["firstname"],
        lastname: json["lastname"],
      );

  Map<String, dynamic> toMap() => {
        "firstname": firstname,
        "lastname": lastname,
      };
}

Future<StaffService> getStaffService() async {
  await Future.delayed(const Duration(seconds: 2));
  return StaffService.fromMap(data); // <- use fromJson if you load data from the JSON.
}

final data = <String, List<Map<String, String>>>{
  "service1": [
    {"firstname": "Peter", "lastname": "Smith"},
    {"firstname": "Paul", "lastname": "Johnson"}
  ],
  "service2": [
    {"firstname": "Mary", "lastname": "Williams"},
    {"firstname": "Guy", "lastname": "Brown"}
  ]
};

复制和粘贴在

In the Dart language, you can use for loop in the list, it makes it easier to work with Flutter UI.

import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'dart:convert';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const RootView(),
    );
  }
}

class RootView extends StatelessWidget {
  const RootView({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: TextButton(
          child: const Text('GO TO SAFF SERVICE'),
          onPressed: () {
            Navigator.push(context, Home.route());
          },
        ),
      ),
    );
  }
}

class Home extends StatefulWidget {
  const Home({Key? key}) : super(key: key);

  static Route route() {
    return CupertinoPageRoute(builder: (_) => const Home());
  }

  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  late Future<StaffService> staffService;

  @override
  void initState() {
    staffService = getStaffService();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Services')),
      body: Center(
        child: FutureBuilder<StaffService>(
          future: staffService,
          builder: (_, snapshot) {
            if (snapshot.hasData) {
              final saff = snapshot.data!;

              return ListView(
                children: [
                  if (saff.service1.isNotEmpty)
                    StaffServiceTile(
                      title: Text(
                        'Title for Service 1',
                        style: Theme.of(context).textTheme.headline5,
                      ),
                      services: saff.service1,
                    ),
                  if (saff.service2.isNotEmpty)
                    StaffServiceTile(
                      title: Text(
                        'Title for Service 2',
                        style: Theme.of(context).textTheme.headline5,
                      ),
                      services: saff.service2,
                    ),
                ],
              );
            } else if (snapshot.hasError) {
              return const Text('Error on loaad data. Try again later.');
            } else {
              return const CircularProgressIndicator();
            }
          },
        ),
      ),
    );
  }
}

class StaffServiceTile extends StatelessWidget {
  const StaffServiceTile({
    Key? key,
    required this.title,
    required this.services,
  }) : super(key: key);

  final Widget title;
  final List<Service> services;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ListTile(title: title),
        for (var person in services)
          ListTile(
            title: Text(
              '${person.firstname} ${person.lastname}',
            ),
          ),
      ],
    );
  }
}

class StaffService {
  StaffService({this.service1 = const [], this.service2 = const []});

  List<Service> service1, service2;

  factory StaffService.fromJson(String str) {
    return StaffService.fromMap(json.decode(str));
  }

  String toJson() => json.encode(toMap());

  factory StaffService.fromMap(Map<String, dynamic> json) => StaffService(
        service1: List<Service>.from(json["service1"].map((x) => Service.fromMap(x))),
        service2: List<Service>.from(json["service2"].map((x) => Service.fromMap(x))),
      );

  Map<String, dynamic> toMap() => {
        "service1": List<dynamic>.from(service1.map((x) => x.toMap())),
        "service2": List<dynamic>.from(service2.map((x) => x.toMap())),
      };
}

class Service {
  Service({this.firstname, this.lastname});

  String? firstname, lastname;

  factory Service.fromJson(String str) => Service.fromMap(json.decode(str));

  String toJson() => json.encode(toMap());

  factory Service.fromMap(Map<String, dynamic> json) => Service(
        firstname: json["firstname"],
        lastname: json["lastname"],
      );

  Map<String, dynamic> toMap() => {
        "firstname": firstname,
        "lastname": lastname,
      };
}

Future<StaffService> getStaffService() async {
  await Future.delayed(const Duration(seconds: 2));
  return StaffService.fromMap(data); // <- use fromJson if you load data from the JSON.
}

final data = <String, List<Map<String, String>>>{
  "service1": [
    {"firstname": "Peter", "lastname": "Smith"},
    {"firstname": "Paul", "lastname": "Johnson"}
  ],
  "service2": [
    {"firstname": "Mary", "lastname": "Williams"},
    {"firstname": "Guy", "lastname": "Brown"}
  ]
};

Copy and paste in the DartPad to test it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文