Flutter Basics pour les débutants (Partie V)

Enfin, nous sommes arrivés à l'un des sujets les plus importants, sans lequel il ne sert à rien d'aller plus loin.





Le plan est assez simple : nous devons nous familiariser avec l'architecture client-serveur et implémenter la liste des postes.





En fin de compte, nous organiserons correctement nos fichiers de page et déplacerons l'élément de liste dans un fichier séparé.





Volons!





Notre plan
  • Partie 1  - introduction au développement, première annexe, notion d'état ;





  • Partie 2  - fichier pubspec.yaml et utilisation de flutter sur la ligne de commande ;





  • Partie 3  - BottomNavigationBar et Navigator ;





  • Partie 4 - MVC. Nous utiliserons ce modèle particulier comme l'un des plus simples ;





  • Partie 5 (article actuel) - package http. Création de la classe Repository, premières demandes, listing des posts ;





  • Partie 6 - travailler avec des formulaires, des zones de texte et créer un article.





  • Partie 7 - travailler avec des images, afficher des images sous forme de grille, recevoir des images du réseau, ajouter les vôtres à l'application;





  • Partie 8 - créer votre propre thème, ajouter des polices et des animations personnalisées ;





  • Partie 9 - un peu sur les tests;





Client et serveur

Le modèle Client/Serveur est au cœur de tout Internet et est le plus répandu.





Quelle est son essence ?





Tout d'abord, voyons ce que sont un client et un serveur :





  • Client - Une machine utilisateur qui envoie des demandes au serveur et reçoit des réponses. Il peut s'agir d'un smartphone, d'un ordinateur ou d'un MacBook.





  • Le serveur est un ordinateur spécial qui contient les données requises par l'utilisateur.





L'ensemble du modèle se résume à un principe primitif : le client envoie une requête, le serveur l'accepte, la traite et transmet la réponse au client.





Pour organiser l'interaction entre le serveur et le client, des protocoles spéciaux sont utilisés. À l'heure actuelle, l'un des protocoles les plus courants sur Internet est http / https (s signifie sécurisé).





http / https vous permet de transférer presque tous les formats de données connus : images, vidéo, texte.





Nous travaillerons avec le format JSON .





JSON est un format de données simple et compréhensible, et surtout léger, car seul le texte est transmis.





Exemple JSON :





[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  },
  {
    "userId": 1,
    "id": 2,
    "title": "qui est esse",
    "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
  },
  ...
]  
      
      



Voici un tableau des messages que nous recevrons du serveur.





: , .





JSON :





{
  "total_items" : 1
  "result" : [
  	{
  		"id" : 1,
  		"name" : "Twillight Sparkle",
  		"pony_type" : "alicorn",
  		"friends" : [
  			"Starlight Glimmer", "Applejack", "Rarity", "Spike"
  		]
		}
  ]
}
      
      



.





.. http / https HTTP .





HTTP :





  • URL - , , . URL : https://jsonplaceholder.typicode.com/posts. ( URL' )





  • , . GET , POST , DELETE - , PUT - .





  • POST, PUT DELETE . GET URL'. : https://jsonplaceholder.typicode.com/posts/1 ( id = 1)





http



.





, pubspec.yaml



:





#  
dependencies:
  flutter:
    sdk: flutter

  #   pub-

  #    
  #    
  flutter_staggered_grid_view: ^0.4.0

  #    MVC 
  mvc_pattern: ^7.0.0

  # http     
	#    
  http: ^0.13.3
      
      



.





post.dart



models



:






//     
class Post {
  //    private
  //     
  final int _userId;
  final int _id;
  final String _title;
  final String _body;
  
  //  getters   
  //      
  int get userId => _userId;
  int get id => _id;
  String get title => _title;
  String get body => _body;

  // Dart      
  //    Post.fromJson(json) -  
  //    JSON      
  //  ,  dynamic  
  //    : String, int, double  ..
  Post.fromJson(Map<String, dynamic> json) :
    this._userId = json["userId"],
    this._id = json["id"],
    this._title = json["title"],
    this._body = json["body"];
}

// PostList     
class PostList {
  final List<Post> posts = [];
  PostList.fromJson(List<dynamic> jsonItems) {
    for (var jsonItem in jsonItems) {
      posts.add(Post.fromJson(jsonItem));
    }
  }
}

//     
//      
// 
abstract class PostResult {}

//    
class PostResultSuccess extends PostResult {
  final PostList postList;
  PostResultSuccess(this.postList);
}

//  
class PostResultFailure extends PostResult {
  final String error;
  PostResultFailure(this.error);
}

//  
class PostResultLoading extends PostResult {
  PostResultLoading();
}
      
      



.





JSON :





{
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
      
      



, userId



id



, title



body



, Post.fromJson(json)



.





Repository



.





data



repository.dart



:





import 'dart:convert';

//  http 
import 'package:http/http.dart' as http;
import 'package:json_placeholder_app/models/post.dart';

//       
//  SERVER
const String SERVER = "https://jsonplaceholder.typicode.com";

class Repository {
  //      
  //   Future ,  
  // fetchPhotos  
  //     UI
  Future<PostList> fetchPosts() async {
    //   URL,  
    //    
    final url = Uri.parse("$SERVER/posts");
    //  GET 
    final response = await http.get(url);
//   
if (response.statusCode == 200) {
  //      
  // json.decode   
  return PostList.fromJson(json.decode(response.body));
} else {
  //      
  throw Exception("failed request");
}

  }
}
      
      



: , ?





.. , .





. URL .





PostController



:





import '../data/repository.dart';
import '../models/post.dart';
import 'package:mvc_pattern/mvc_pattern.dart';

class PostController extends ControllerMVC {
  //   
  final Repository repo = new Repository();

  //   
  PostController();
  
  //   -  
  PostResult currentState = PostResultLoading();

  void init() async {
    try {
      //    
      final postList = await repo.fetchPosts();
      //        
      setState(() => currentState = PostResultSuccess(postList));
    } catch (error) {
      //     
      setState(() => currentState = PostResultFailure(" "));
    }
  }


}
      
      



: :






import 'package:flutter/material.dart';
import '../controllers/post_controller.dart';
import '../models/post.dart';
import 'package:mvc_pattern/mvc_pattern.dart';

class PostListPage extends StatefulWidget {
  @override
  _PostListPageState createState() => _PostListPageState();
}

//     StateMVC
class _PostListPageState extends StateMVC {

  //    
  PostController _controller;

  //    StateMVC  
  //    
  _PostListPageState() : super(PostController()) {
    _controller = controller as PostController;
  }

  //   
  //     
  @override
  void initState() {
    super.initState();
    _controller.init();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Post List Page"),
      ),
      body: _buildContent()
    );
  }

  Widget _buildContent() {
    //     
    final state = _controller.currentState;
    if (state is PostResultLoading) {
      // 
      return Center(
        child: CircularProgressIndicator(),
      );
    } else if (state is PostResultFailure) {
      // 
      return Center(
        child: Text(
          state.error,
          textAlign: TextAlign.center,
          style: Theme.of(context).textTheme.headline4.copyWith(color: Colors.red)
        ),
      );
    } else {
      //   
      final posts = (state as PostResultSuccess).postList.posts;
      return Padding(
        padding: EdgeInsets.all(10),
        // ListView.builder   
        //      
        child: ListView.builder(
          itemCount: posts.length,
          itemBuilder: (context, index) {
            return _buildPostItem(posts[index]);
          },
        ),
      );
    }
  }

  //   
  Widget _buildPostItem(Post post) {
    return Container(
        decoration: BoxDecoration(
            borderRadius: BorderRadius.all(Radius.circular(15)),
            border: Border.all(color: Colors.grey.withOpacity(0.5), width: 0.3)
        ),
        margin: EdgeInsets.only(bottom: 10),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Container(
              decoration: BoxDecoration(
                borderRadius: BorderRadius.only(topLeft: Radius.circular(15), topRight: Radius.circular(15)),
                color: Theme.of(context).primaryColor,
              ),
              padding: EdgeInsets.all(10),
              child: Text(
                post.title,
                textAlign: TextAlign.left,
                style: Theme.of(context).textTheme.headline5.copyWith(color: Colors.white),),
            ),
            Container(
              child: Text(
                post.body,
                style: Theme.of(context).textTheme.bodyText2,
              ),
              padding: EdgeInsets.all(10),
            ),
          ],
        )
    );
  }

}
      
      



.





, )





:





! :





!





.





post_list_page.dart



110 , . 10 20 !





, .





.





Widget _buildItem(post)



.





:





post post_list_item.dart:











import 'package:flutter/material.dart';

import '../../models/post.dart';

//  
class PostListItem extends StatelessWidget {
  
  final Post post;
  
  //     
  PostListItem(this.post);
  
  Widget build(BuildContext context) {
    return Container(
        decoration: BoxDecoration(
            borderRadius: BorderRadius.all(Radius.circular(15)),
            border: Border.all(color: Colors.grey.withOpacity(0.5), width: 0.3)
        ),
        margin: EdgeInsets.only(bottom: 10),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Container(
              decoration: BoxDecoration(
                borderRadius: BorderRadius.only(topLeft: Radius.circular(15), topRight: Radius.circular(15)),
                color: Theme.of(context).primaryColor,
              ),
              padding: EdgeInsets.all(10),
              child: Text(
                post.title,
                textAlign: TextAlign.left,
                style: Theme.of(context).textTheme.headline5.copyWith(color: Colors.white),),
            ),
            Container(
              child: Text(
                post.body,
                style: Theme.of(context).textTheme.bodyText2,
              ),
              padding: EdgeInsets.all(10),
            ),
          ],
        )
    );
  }
}
      
      



post_list_page.dart



:






import 'package:flutter/material.dart';
import '../../controllers/post_controller.dart';
import '../../models/post.dart';
import 'post_list_item.dart';
import 'package:mvc_pattern/mvc_pattern.dart';

class PostListPage extends StatefulWidget {
  @override
  _PostListPageState createState() => _PostListPageState();
}

//     StateMVC
class _PostListPageState extends StateMVC {

  //    
  PostController _controller;

  //    StateMVC  
  //    
  _PostListPageState() : super(PostController()) {
    _controller = controller as PostController;
  }

  //   
  //     
  @override
  void initState() {
    super.initState();
    _controller.init();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Post List Page"),
      ),
      body: _buildContent()
    );
  }

  Widget _buildContent() {
    //     
    final state = _controller.currentState;
    if (state is PostResultLoading) {
      // 
      return Center(
        child: CircularProgressIndicator(),
      );
    } else if (state is PostResultFailure) {
      // 
      return Center(
        child: Text(
          state.error,
          textAlign: TextAlign.center,
          style: Theme.of(context).textTheme.headline4.copyWith(color: Colors.red)
        ),
      );
    } else {
      //   
      final posts = (state as PostResultSuccess).postList.posts;
      return Padding(
        padding: EdgeInsets.all(10),
        // ListView.builder   
        //      
        child: ListView.builder(
          itemCount: posts.length,
          itemBuilder: (context, index) {
            //     
            //  
            return PostListItem(posts[index]);
          },
        ),
      );
    }
  }

  
}
      
      



.





J'ai essayé de raconter brièvement et de montrer par un exemple illustratif comment travailler avec le réseau.





J'espère que mon article vous a été utile)





Lien vers Github





Bon code à tous !








All Articles