Dasar Flutter untuk Pemula (Bagian V)

Akhirnya, kita sampai pada salah satu topik yang paling penting, yang tanpanya tidak ada gunanya melangkah lebih jauh.





Rencananya cukup sederhana: kita harus mengenal arsitektur client-server dan mengimplementasikan daftar posting.





Pada akhirnya, kami akan mengatur file halaman kami dengan benar dan memindahkan item daftar ke file terpisah.





Ayo terbang!





Rencana kita
  • Bagian 1  - pengantar pembangunan, lampiran pertama, konsep negara;





  • Bagian 2  - file pubspec.yaml dan menggunakan flutter pada baris perintah;





  • Bagian 3  - BottomNavigationBar dan Navigator;





  • Bagian 4 - MVC. Kami akan menggunakan pola khusus ini sebagai salah satu yang paling sederhana;





  • Bagian 5 (artikel saat ini) - paket http. Pembuatan kelas Repositori, permintaan pertama, daftar posting;





  • Bagian 6 - bekerja dengan formulir, kotak teks dan membuat posting.





  • Bagian 7 - bekerja dengan gambar, menampilkan gambar dalam bentuk kisi, menerima gambar dari jaringan, menambahkan gambar Anda sendiri ke aplikasi;





  • Bagian 8 - membuat tema Anda sendiri, menambahkan font dan animasi khusus;





  • Bagian 9 - sedikit tentang pengujian;





Klien dan Server

Model Klien / Server adalah jantung dari seluruh Internet dan merupakan yang paling umum.





Apa esensinya?





Pertama, mari kita cari tahu apa itu klien dan server:





  • Klien - Perangkat pengguna yang mengirim permintaan ke server dan menerima tanggapan. Ini bisa berupa smartphone, komputer, atau MacBook.





  • Server adalah komputer khusus yang berisi data-data yang dibutuhkan oleh pengguna.





Seluruh model bermuara pada prinsip primitif: klien mengirim permintaan, server menerimanya, memprosesnya, dan mengirimkan respons ke klien.





Untuk mengatur interaksi antara server dan klien, protokol khusus digunakan. Saat ini, salah satu protokol paling umum di Internet adalah http / https (s singkatan dari secure).





http / https memungkinkan Anda untuk mentransfer hampir semua format data yang dikenal: gambar, video, teks.





Kami akan bekerja dengan format JSON .





JSON adalah format data yang sederhana dan mudah dipahami, dan yang terpenting, ringan, karena hanya teks yang ditransmisikan.





Contoh 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"
  },
  ...
]  
      
      



Berikut adalah array posting yang akan kami terima dari server.





: , .





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]);
          },
        ),
      );
    }
  }

  
}
      
      



.





Saya mencoba memberi tahu secara singkat dan menunjukkan dengan contoh ilustratif bagaimana bekerja dengan jaringan.





Saya harap artikel saya bermanfaat bagi Anda)





Tautan ke Github





Kode yang bagus semuanya!








All Articles