#json #flutter #api #flutter-getx
#json #трепетать #API #флаттер-геткс
Вопрос:
Я все еще новичок в разработке flutter и недавно пытался вызвать API. Мне удалось сделать это, не помещая данные в модель, и это сработало нормально. Проблема в том, когда я пытаюсь сериализовать данные. Я создал модель, используя app.quicktype.io. и я не уверен, как это назвать в моем файле представлений.
В проекте используется Getx.
Менеджер API
import 'package:api_test_2/models/event.dart'; import 'package:http/http.dart' as http; class ApiManager { static var client = http.Client(); // Future as lt;Listlt;Eventgt;gt; maybe ? static Futurelt;Event?gt; fetchEvents() async { var response = await client.get(Uri.parse( 'https://xposure.ae/wp-json/wp/auditorium/v1/events')); if (response.statusCode == 200) { var jsonString = response.body; print(jsonString); return eventFromJson(jsonString); } else { //show error message return null; } } }
Модель событий
// To parse this JSON data, do // // final event = eventFromJson(jsonString); import 'package:meta/meta.dart'; import 'dart:convert'; Event eventFromJson(String str) =gt; Event.fromJson(json.decode(str)); String eventToJson(Event data) =gt; json.encode(data.toJson()); class Event { Event({ required this.data, }); Listlt;Datumgt;? data; factory Event.fromJson(Maplt;String, dynamicgt; json) =gt; Event( data: json["data"] == null ? null : Listlt;Datumgt;.from(json["data"].map((x) =gt; Datum.fromJson(x))), ); //null check added after data Maplt;String, dynamicgt; toJson() =gt; { "data": data == null ? null : Listlt;dynamicgt;.from(data!.map((x) =gt; x.toJson())), }; } class Datum { Datum({ required this.eventtitle, required this.description, required this.eventImage, required this.speaker, required this.datetime, required this.location, }); String? eventtitle; String description; String? eventImage; Speaker? speaker; String? datetime; Location? location; factory Datum.fromJson(Maplt;String, dynamicgt; json) =gt; Datum( eventtitle: json["Eventtitle"] == null ? null : json["Eventtitle"], description: json["Description"] == null ? null : json["Description"], eventImage: json["event_image"] == null ? null : json["event_image"], speaker: json["Speaker"] == null ? null : Speaker.fromJson(json["Speaker"]), datetime: json["datetime"] == null ? null : json["datetime"], location: json["Location"] == null ? null : Location.fromJson(json["Location"]), ); Maplt;String, dynamicgt; toJson() =gt; { "Eventtitle": eventtitle == null ? null : eventtitle, "Description": description == null ? null : description, "event_image": eventImage == null ? null : eventImage, "Speaker": speaker == null ? null : speaker!.toJson(), "datetime": datetime == null ? null : datetime, "Location": location == null ? null : location!.toJson(), }; } class Location { Location({ required this.venue, required this.address, }); Venue? venue; Address? address; factory Location.fromJson(Maplt;String, dynamicgt; json) =gt; Location( venue: json["venue"] == null ? null : venueValues.map[json["venue"]], address: json["address"] == null ? null : addressValues.map[json["address"]], ); //added null checks after reverse Maplt;String, dynamicgt; toJson() =gt; { "venue": venue == null ? null : venueValues.reverse![venue], "address": address == null ? null : addressValues.reverse![address], }; } enum Address { SHARJAH_BR_SHARJAH_BR_61110_BR_UNITED_ARAB_EMIRATES } final addressValues = EnumValues({ "Sharjahlt;/brgt;Sharjah,lt;/brgt;61110,lt;/brgt;United Arab Emirates": Address.SHARJAH_BR_SHARJAH_BR_61110_BR_UNITED_ARAB_EMIRATES }); enum Venue { XPOSURE_INTERNATIONAL_PHOTOGRAPHY_FESTIVAL } final venueValues = EnumValues({ "Xposure International Photography Festival": Venue.XPOSURE_INTERNATIONAL_PHOTOGRAPHY_FESTIVAL }); class Speaker { Speaker({ required this.speakername, required this.link, }); String speakername; String link; factory Speaker.fromJson(Maplt;String, dynamicgt; json) =gt; Speaker( speakername: json["speakername"] == null ? null : json["speakername"], link: json["link"] == null ? null : json["link"], ); Maplt;String, dynamicgt; toJson() =gt; { "speakername": speakername == null ? null : speakername, "link": link == null ? null : link, }; } class EnumValueslt;Tgt; { Maplt;String, Tgt; map; Maplt;T, Stringgt;? reverseMap; EnumValues(this.map); Maplt;T, Stringgt;? get reverse { if (reverseMap == null) { reverseMap = map.map((k, v) =gt; new MapEntry(v, k)); } return reverseMap; } }
Controller
import 'package:api_test_2/models/event.dart'; import 'package:api_test_2/services/api_manager.dart'; import 'package:get/state_manager.dart'; class EventController extends GetxController { var isLoading = true.obs; // var eventList = Listlt;Eventgt;().obs; var eventList = lt;Eventgt;[].obs; @override void onInit() { fetchEvents(); super.onInit(); } void fetchEvents() async { try { isLoading(true); var events = await ApiManager.fetchEvents(); if (events != null) { // added cast as Listlt;Eventgt; eventList.value = events as Listlt;Eventgt;; } } finally { isLoading(false); } } }
Main view
import 'package:api_test_2/controllers/event_controller.dart'; import 'package:api_test_2/views/event_tile.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:get/get.dart'; import 'package:get/instance_manager.dart'; class HomePage extends StatelessWidget { final EventController eventController = Get.put(EventController()); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( elevation: 0, leading: const Icon( Icons.arrow_back_ios, ), actions: [ IconButton( icon: const Icon( Icons.shopping_cart, ), onPressed: () {}, ) ], ), body: Column( children: [ Padding( padding: const EdgeInsets.all(16), child: Row( children: [ const Expanded( child: Text( 'ShopX', style: TextStyle( fontFamily: 'avenir', fontSize: 32, fontWeight: FontWeight.w900), ), ), IconButton( icon: const Icon(Icons.view_list_rounded), onPressed: () {}), IconButton(icon: const Icon(Icons.grid_view), onPressed: () {}), ], ), ), Expanded( child: Obx(() { if (eventController.isLoading.value) return Center(child: CircularProgressIndicator()); else { return StaggeredGridView.countBuilder( crossAxisCount: 1, itemCount: eventController.eventList.length, crossAxisSpacing: 16, mainAxisSpacing: 16, itemBuilder: (context, index) { return EventTile(eventController.eventList[index]); }, staggeredTileBuilder: (index) =gt; StaggeredTile.fit(1), ); } }), ) ], ), ); } }
Плитка событий
import 'package:api_test_2/models/event.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; class EventTile extends StatelessWidget { final Event event; const EventTile(this.event); @override Widget build(BuildContext context) { return Card( elevation: 2, child: Padding( padding: const EdgeInsets.all(8.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Text( // ), ], ), ), ); } }
Я думаю, что делаю что-то не так с преобразованием в список или карту, но я не уверен, находится ли это в моем классе API manager или в самой модели событий. Если бы кто-нибудь мог помочь, я был бы так благодарен.
Комментарии:
1. Вы извлекаете данные из API и отображаете их в виджетах?
2. Да, я могу извлекать данные и отображать их в виджетах. Проблема в том, что я пытаюсь поместить его в класс модели, а затем вызвать модель в виджет.
Ответ №1:
Ваш фактический список событий находится внутри data
свойства вашей модели. Поэтому ваш контроллер должен быть:
class EventController extends GetxController { final isLoading = true.obs; final event = Rxnlt;Eventgt;(); @override void onInit() async{ await fetchEvents(); super.onInit(); } void fetchEvents() async { try { isLoading(true); var response = await ApiManager.fetchEvents(); if (response != null) { event.value = response; } } finally { isLoading(false); } } }
И твой StaggeredGridView
:
return StaggeredGridView.countBuilder( crossAxisCount: 1, itemCount: eventController.event.value.data.length, crossAxisSpacing: 16, mainAxisSpacing: 16, itemBuilder: (context, index) { return EventTile(eventController.event.value.data[index]); }, staggeredTileBuilder: (index) =gt; StaggeredTile.fit(1), );
И, наконец, ваш EventTile
:
class EventTile extends StatelessWidget { final Datum datum; const EventTile(this.datum); @override Widget build(BuildContext context) { return Card( elevation: 2, child: Padding( padding: const EdgeInsets.all(8.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Text( // ), ], ), ), ); } }
Комментарии:
1. Большое вам спасибо! Я целую вечность не мог этого понять.
2. Исправлено? Если это так, пожалуйста, примите это как ответ.
3. Да, сейчас это работает. Еще раз спасибо