Lấy dữ liệu từ API và trả về Json trong Flutter

📅 — 👀 428 — 👦

Mục lục :


Trang này có hữu ích không?

👍 Có (101)        👎 Không (105)


Đây là đoạn code đơn giản lấy dữ liệu từ api và trả về dạng json trong Flutter. Các bạn có thể tham khảo cách làm bên dưới.

Chú ý: Nếu bạn debug App trên trình duyệt Chrome hay Edge thì sẽ gặp lỗi XMLHttpRequest error (đây là cơ chế chặn CORS – Cross-origin resource sharing của trình duyệt). Chúng ta bỏ qua lỗi này như sau :

  1. Vào thư mục flutter\bin\cache và xóa file có tên flutter_tools.stamp
  2. Vào thư mục flutter\packages\flutter_tools\lib\src\web và mở file chrome.dart
  3. Tìm ‘–disable-extensions’
  4. Thêm ‘–disable-web-security’

https://thichcode.net/api/users.php
Dữ liệu trả về có dạng sau:

[
  {
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret",
    "email": "Sincere@april.biz",
    "address": {
      "street": "Kulas Light",
      "suite": "Apt. 556",
      "city": "Gwenborough",
      "zipcode": "92998-3874",
      "geo": {
        "lat": "-37.3159",
        "lng": "81.1496"
      }
    },
    "phone": "1-770-736-8031 x56442",
    "website": "hildegard.org",
    "company": {
      "name": "Romaguera-Crona",
      "catchPhrase": "Multi-layered client-server neural-net",
      "bs": "harness real-time e-markets"
    }
  },
  {
    "id": 2,
    "name": "Ervin Howell",
    "username": "Antonette",
    "email": "Shanna@melissa.tv",
    "address": {
      "street": "Victor Plains",
      "suite": "Suite 879",
      "city": "Wisokyburgh",
      "zipcode": "90566-7771",
      "geo": {
        "lat": "-43.9509",
        "lng": "-34.4618"
      }
    },
    "phone": "010-692-6593 x09125",
    "website": "anastasia.net",
    "company": {
      "name": "Deckow-Crist",
      "catchPhrase": "Proactive didactic contingency",
      "bs": "synergize scalable supply-chains"
    }
  },
  {
    "id": 3,
    "name": "Clementine Bauch",
    "username": "Samantha",
    "email": "Nathan@yesenia.net",
    "address": {
      "street": "Douglas Extension",
      "suite": "Suite 847",
      "city": "McKenziehaven",
      "zipcode": "59590-4157",
      "geo": {
        "lat": "-68.6102",
        "lng": "-47.0653"
      }
    },
    "phone": "1-463-123-4447",
    "website": "ramiro.info",
    "company": {
      "name": "Romaguera-Jacobson",
      "catchPhrase": "Face to face bifurcated interface",
      "bs": "e-enable strategic applications"
    }
  },
  {
    "id": 4,
    "name": "Patricia Lebsack",
    "username": "Karianne",
    "email": "Julianne.OConner@kory.org",
    "address": {
      "street": "Hoeger Mall",
      "suite": "Apt. 692",
      "city": "South Elvis",
      "zipcode": "53919-4257",
      "geo": {
        "lat": "29.4572",
        "lng": "-164.2990"
      }
    },
    "phone": "493-170-9623 x156",
    "website": "kale.biz",
    "company": {
      "name": "Robel-Corkery",
      "catchPhrase": "Multi-tiered zero tolerance productivity",
      "bs": "transition cutting-edge web services"
    }
  },
  {
    "id": 5,
    "name": "Chelsey Dietrich",
    "username": "Kamren",
    "email": "Lucio_Hettinger@annie.ca",
    "address": {
      "street": "Skiles Walks",
      "suite": "Suite 351",
      "city": "Roscoeview",
      "zipcode": "33263",
      "geo": {
        "lat": "-31.8129",
        "lng": "62.5342"
      }
    },
    "phone": "(254)954-1289",
    "website": "demarco.info",
    "company": {
      "name": "Keebler LLC",
      "catchPhrase": "User-centric fault-tolerant solution",
      "bs": "revolutionize end-to-end systems"
    }
  },
  {
    "id": 6,
    "name": "Mrs. Dennis Schulist",
    "username": "Leopoldo_Corkery",
    "email": "Karley_Dach@jasper.info",
    "address": {
      "street": "Norberto Crossing",
      "suite": "Apt. 950",
      "city": "South Christy",
      "zipcode": "23505-1337",
      "geo": {
        "lat": "-71.4197",
        "lng": "71.7478"
      }
    },
    "phone": "1-477-935-8478 x6430",
    "website": "ola.org",
    "company": {
      "name": "Considine-Lockman",
      "catchPhrase": "Synchronised bottom-line interface",
      "bs": "e-enable innovative applications"
    }
  },
  {
    "id": 7,
    "name": "Kurtis Weissnat",
    "username": "Elwyn.Skiles",
    "email": "Telly.Hoeger@billy.biz",
    "address": {
      "street": "Rex Trail",
      "suite": "Suite 280",
      "city": "Howemouth",
      "zipcode": "58804-1099",
      "geo": {
        "lat": "24.8918",
        "lng": "21.8984"
      }
    },
    "phone": "210.067.6132",
    "website": "elvis.io",
    "company": {
      "name": "Johns Group",
      "catchPhrase": "Configurable multimedia task-force",
      "bs": "generate enterprise e-tailers"
    }
  },
  {
    "id": 8,
    "name": "Nicholas Runolfsdottir V",
    "username": "Maxime_Nienow",
    "email": "Sherwood@rosamond.me",
    "address": {
      "street": "Ellsworth Summit",
      "suite": "Suite 729",
      "city": "Aliyaview",
      "zipcode": "45169",
      "geo": {
        "lat": "-14.3990",
        "lng": "-120.7677"
      }
    },
    "phone": "586.493.6943 x140",
    "website": "jacynthe.com",
    "company": {
      "name": "Abernathy Group",
      "catchPhrase": "Implemented secondary concept",
      "bs": "e-enable extensible e-tailers"
    }
  },
  {
    "id": 9,
    "name": "Glenna Reichert",
    "username": "Delphine",
    "email": "Chaim_McDermott@dana.io",
    "address": {
      "street": "Dayna Park",
      "suite": "Suite 449",
      "city": "Bartholomebury",
      "zipcode": "76495-3109",
      "geo": {
        "lat": "24.6463",
        "lng": "-168.8889"
      }
    },
    "phone": "(775)976-6794 x41206",
    "website": "conrad.com",
    "company": {
      "name": "Yost and Sons",
      "catchPhrase": "Switchable contextually-based project",
      "bs": "aggregate real-time technologies"
    }
  },
  {
    "id": 10,
    "name": "Clementina DuBuque",
    "username": "Moriah.Stanton",
    "email": "Rey.Padberg@karina.biz",
    "address": {
      "street": "Kattie Turnpike",
      "suite": "Suite 198",
      "city": "Lebsackbury",
      "zipcode": "31428-2261",
      "geo": {
        "lat": "-38.2386",
        "lng": "57.2232"
      }
    },
    "phone": "024-648-3804",
    "website": "ambrose.net",
    "company": {
      "name": "Hoeger LLC",
      "catchPhrase": "Centralized empowering task-force",
      "bs": "target end-to-end models"
    }
  }
]

Tạo file models.dart

import 'dart:convert';
import 'package:flutter/foundation.dart';

class CategoryProvider with ChangeNotifier {
  UserInfo _selectedItem = UserInfo(
      id: 0,
      name: '',
      username: '',
      email: '',
      address: Address(
          street: 'street',
          suite: 'suite',
          city: 'city',
          zipcode: 'zipcode',
          geo: Geo(lat: 'lat', lng: 'lng')),
      phone: '',
      website: '',
      company: Company(name: 'name', catchPhrase: 'catchPhrase', bs: 'bs'));

  UserInfo get selected => _selectedItem;

  void setSelectedItem(UserInfo s) {
    _selectedItem = s;
    notifyListeners();
  }
}

List usersFromJson(String str) =>
    List.from(json.decode(str).map((x) => UserInfo.fromJson(x)));

String usersToJson(List data) =>
    json.encode(List.from(data.map((x) => x.toJson())));

class UserInfo {
  int id;
  String name;
  String username;
  String email;
  Address address;
  String phone;
  String website;
  Company company;

  UserInfo({
    required this.id,
    required this.name,
    required this.username,
    required this.email,
    required this.address,
    required this.phone,
    required this.website,
    required this.company,
  });

  factory UserInfo.fromJson(Map<String, dynamic> json) => UserInfo(
        id: json["id"],
        name: json["name"],
        username: json["username"],
        email: json["email"],
        address: Address.fromJson(json["address"]),
        phone: json["phone"],
        website: json["website"],
        company: Company.fromJson(json["company"]),
      );

  Map<String, dynamic> toJson() => {
        "id": id.toString(),
        "name": name,
        "username": username,
        "email": email,
        "address": address.toString(),
        "phone": phone,
        "website": website,
        "company": company.toJson(),
      };
}

class Company {
  String name;
  String catchPhrase;
  String bs;

  Company({
    required this.name,
    required this.catchPhrase,
    required this.bs,
  });

  factory Company.fromJson(Map<String, dynamic> json) => Company(
        name: json["name"],
        catchPhrase: json["catchPhrase"],
        bs: json["bs"],
      );

  Map<String, dynamic> toJson() => {
        "name": name,
        "catchPhrase": catchPhrase,
        "bs": bs,
      };
}

class Geo {
  String lat;
  String lng;

  Geo({
    required this.lat,
    required this.lng,
  });

  factory Geo.fromJson(Map<String, dynamic> json) => Geo(
        lat: json["lat"],
        lng: json["lng"],
      );

  Map<String, dynamic> toJson() => {
        "lat": lat,
        "lng": lng,
      };
}

class Address {
  String street;
  String suite;
  String city;
  String zipcode;
  Geo geo;

  Address({
    required this.street,
    required this.suite,
    required this.city,
    required this.zipcode,
    required this.geo,
  });

  factory Address.fromJson(Map<String, dynamic> json) => Address(
        street: json["street"],
        suite: json["suite"],
        city: json["city"],
        zipcode: json["zipcode"],
        geo: Geo.fromJson(json["geo"]),
      );

  Map<String, dynamic> toJson() => {
        "street": street,
        "suite": suite,
        "city": city,
        "zipcode": zipcode,
        "geo": geo,
      };
}

Tạo file main.dart

import 'package:flutter/material.dart';
import 'package:pretty_json/pretty_json.dart';
import 'package:provider/provider.dart';
import 'package:http/http.dart' as http;

import 'models.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '[ThichCode.NET] Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ChangeNotifierProvider(
          create: (_) => CategoryProvider(),
          builder: (context, snapshot) {
            return const JsonApiDropdown();
          }),
    );
  }
}

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

  @override
  JsonApiDropdownState createState() {
    return JsonApiDropdownState();
  }
}

class JsonApiDropdownState extends State {
  Future<List>? _listFuture;
  Future<List> _fetchUsers() async {
    final jsonString = await http
        .get(Uri.parse('https://thichcode.net/api/users.php'), headers: {
      "Accept": "application/json",
      "Access-Control_Allow_Origin": "*",
      "Content-Type": "application/json",
    });
    if (jsonString.statusCode == 404) {
      // ignore: avoid_print
      print('Not connect');
      return [];
    } else {
      final users = usersFromJson(jsonString.body);
      if (users.isNotEmpty) {
        context.read().setSelectedItem(users[0]);
      }
      return users;
    }
  }

  @override
  void initState() {
    super.initState();
    _listFuture = _fetchUsers();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Fetching data from JSON - ThichCode.NET'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        mainAxisSize: MainAxisSize.max,
        children: [
          FutureBuilder<List>(
            future: _listFuture,
            builder:
                (BuildContext context, AsyncSnapshot<List> snapshot) {
              if (!snapshot.hasData) {
                return const CircularProgressIndicator();
              }
              var arrItems = snapshot.data!
                  .map(
                    (u) => DropdownMenuItem(
                      child: Text(u.name),
                      value: u,
                    ),
                  )
                  .toList();
              return Consumer(
                  builder: (context, provider, _) {
                return DropdownButton(
                  items: arrItems,
                  onChanged: (UserInfo? _value) => {
                    context.read().setSelectedItem(_value!),
                  },
                  isExpanded: false,
                  value: Provider.of(context).selected,
                  hint: const Text('Select User'),
                );
              });
            },
          ),
          Padding(
            padding: const EdgeInsets.all(30.0),
            child: Text(
              prettyJson(Provider.of(context).selected,
                  indent: 5),
            ),
          ),
        ],
      ),
    );
  }
}

Chúc các bạn thành công !


📁 Flutter