Home > Back-end >  Get strapi datas into Flutter
Get strapi datas into Flutter

Time:05-25

*After many documentations readed, I saw that Flutter is not compatible with strapi v4, to use it with Flutter, you have to use a strapi project under v4.

I'm trying to connect my Flutter app to Strapi.

I followed the official Strapi tuto for flutter and some videos on Youtube about it but I'm stuck to read datas.

I have this error while my view begins:

_TypeError (type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Iterable')

This is my full code for this view:

import 'dart:convert';
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:strapitests/user.dart';

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

  @override
  State<MyList> createState() => _MyListState();
}

class _MyListState extends State<MyList> {
  List<User> users = [];

  Future getAll() async {
    var data = await http.get(Uri.parse("http://10.0.2.2:1337/api/apis"));
    var jsonData = json.decode(data.body);

    for (var u in jsonData) {
      users.add(
        u['name'],
      );
    }
    return users;
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: FutureBuilder(
        future: getAll(),
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          if (snapshot.data == null) {
            return Container(
              child: const Center(
                child: Text("Loading..."),
              ),
            );
          } else {
            return ListView.builder(
              itemCount: snapshot.data.length,
              itemBuilder: (BuildContext context, int index) {
                return ListTile(
                  title: Text(snapshot.data[index].name),
                  subtitle: Text(snapshot.data[index].email),
                );
              },
            );
          }
        },
      ),
    );
  }
}

And this is my 'User' class:

class User {
  String name;
  String email;
  String password;
  User(this.name, this.email, this.password);
}

While i make a 'GET' on my browser, the result is:

"data": [
{
"id": 1,
"attributes": {
"name": "john",
"password": "dfdf",
"email": "[email protected]",
"createdAt": "2022-05-23T20:38:27.725Z",
"updatedAt": "2022-05-23T20:38:28.466Z",
"publishedAt": "2022-05-23T20:38:28.464Z"
}
},
{
"id": 2,
"attributes": {
"name": "text",
"password": "mp",
"email": "mail",
"createdAt": "2022-05-23T20:47:56.717Z",
"updatedAt": "2022-05-23T20:47:56.717Z",
"publishedAt": "2022-05-23T20:47:56.712Z"
}
},
{
"id": 3,
"attributes": {
"name": "name",
"password": "mp",
"email": "mail",
"createdAt": "2022-05-23T20:52:07.911Z",
"updatedAt": "2022-05-23T20:52:07.911Z",
"publishedAt": "2022-05-23T20:52:07.910Z"
}
}
],

Thanks for helping!

CodePudding user response:

First, you will need to decode your users from JSON. Since this is a simple class, you can just write a quick fromJson constructor for your User class:

class User {
  String name;
  String email;
  String password;
  User(this.name, this.email, this.password);

  factory User.fromJson(Map<String, dynamic> json) {
    final attributes = json['attributes'];
    return User(
      attributes['name'],
      attributes['email'],
      attributes['password'],
    );
  }
}

Next, the data you're receiving is a map, which cannot be iterated through with a for-loop.

Instead, iterate over the list keyed by "data", and decode each element with the User.fromJson constructor we just defined:

Future<List<User>> getAll() async {
  var data = await http.get(Uri.parse("http://10.0.2.2:1337/api/apis"));
  var jsonData = json.decode(data.body);

  final users = jsonData['data'];

  return users.map((userJson) => User.fromJson(userJson)).toList();
}

Finally, since you're using a FutureBuilder, you actually don't need this to be a stateful widget, and you don't need to store users as a property on your class. You can simply use the list returned in the snapshot - Though you'll need change your code so that the Future is a final member so that the widget doesn't construct a new future on each build:

class MyList extends StatelessWidget {
  late final Future<List<User>> users = getAll();

  @override
  Widget build(BuildContext context) {
    return Container(
      child: FutureBuilder(
        future: users,
        // ...
      ),
    );
  }
}

Also — and this is beside the point in terms of your question — but it's a good idea to look into ways of avoiding storing passwords on your server. If you do store passwords, definitely avoid returning them in any API responses for a production app :).

Here are a couple of good articles on the topic:

https://auth0.com/blog/hashing-passwords-one-way-road-to-security/

https://auth0.com/blog/adding-salt-to-hashing-a-better-way-to-store-passwords/

CodePudding user response:

I followed your code and improve my 'User' call but now it returns this error:

_TypeError (type 'List' is not a subtype of type 'FutureOr<List>')

This is my updated code:

import 'dart:convert';
import 'dart:async';

import 'package:provider/provider.dart';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:strapitests/user.dart';

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

  Future<List<User>> getAll() async {
    var data = await http.get(Uri.parse("http://10.0.2.2:1337/api/apis"));
    var jsonData = json.decode(data.body);

    final users = jsonData['data'];

    return users.map((userJson) => User.fromJson(userJson)).toList();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        child: FutureBuilder(
          future: getAll(),
          builder: (BuildContext context, AsyncSnapshot snapshot) {
            if (snapshot.data == null) {
              return Container(
                child: const Center(
                  child: Text("Loading..."),
                ),
              );
            } else {
              return ListView.builder(
                itemCount: snapshot.data.length,
                itemBuilder: (BuildContext context, int index) {
                  return ListTile(
                    title: Text(snapshot.data[index].name),
                    subtitle: Text(snapshot.data[index].email),
                  );
                },
              );
            }
          },
        ),
      ),
    );
  }
}
  • Related