Halo teman – teman pada seri artikel kali ini kita akan melanjutkan tutorialnya, setelah kalian sudah install depedensi dan konfigurasi untuk komunikasi rest API, langkah selanjutnya yaitu kita akan menampilkan data dari Rest API.
Langkah 1 – Buat Layout Post
Sekarang teman – teman buat file dengan nama post_view.dart
pada folder component
, lalu kalian buat class dengan nama PostView
dengan extends StatelessWidget
, kalian bisa ketik aja stl nanti akan muncul sugest untuk pembuat class seperti gambar berikut:
lalu lengkapi kode berikut:
import 'package:flutter/material.dart';
import 'package:flutter_pemula/model/post.dart';
class PostView extends StatelessWidget {
final Post post;
const PostView({
super.key,
required this.post,
});
@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Image.network(
post.image,
height: 150,
width: 150,
fit: BoxFit.cover,
),
const SizedBox(
width: 16,
),
Expanded(
flex: 3,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
post.title,
maxLines: 1,
style: Theme.of(context).textTheme.bodyLarge,
),
Text(post.content),
],
),
)
],
);
}
}
Langkah 2 – Membuat Tampilan Home
Setelah kalian sudah membuat tampilan post sekarang kita membuat screen untuk menampilkan data dari json, sekarang kita buat class dengan nama home.dart
pada folder screen
, lalu kalian buat class dengan nama Home
dengan extends StatefulWidget
.
Perhatian:
Apa perbedaan
StatelessWidget
denganStatefulWidget
secara singkatStatelessWidget
adalah widget yang besifat tidak berubah immutable. Artinya, sekali widget ini dibuat, ia tidak akan berubah selama siklus hidupnya. Sedangkan,StatefulWidget
adalah widget yang bersifat dapat berubah mutable. Artinya, widget ini bisa berubah selama siklus hidupnya melalui perubahan state (keadaan).
Ketik kode berikut:
import 'package:flutter/material.dart';
class Home extends StatefulWidget {
const Home({super.key});
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}
Lagkah 3 – Membuat Request GET Data dari API
Langkah selajutnya buat request get data dari API lalu di tampilkan, sehingga kode secara lengkap menjadi berikut:
import 'package:flutter/material.dart';
import 'package:flutter_pemula/api/repository.dart';
import 'package:flutter_pemula/component/post_view.dart';
import 'package:flutter_pemula/model/post.dart';
class Home extends StatefulWidget {
final String title;
const Home({
super.key,
required this.title,
});
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
final ScrollController _scrollController = ScrollController();
final Repository _apiService = Repository();
final List<Post> _posts = [];
bool _isLoading = false;
bool _hasMore = true;
int _currentPage = 1;
@override
void initState() {
super.initState();
_loadPosts();
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
_loadMorePosts();
}
});
}
Future<void> _loadPosts() async {
setState(() {
_isLoading = true;
});
try {
final result = await _apiService.fetchPosts(_currentPage);
setState(() {
_currentPage++;
_posts.addAll(result['posts']);
_hasMore = result['nextPageUrl'] != null;
});
} catch (e) {
throw Exception(e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
Future<void> _loadMorePosts() async {
if (_isLoading || !_hasMore) return;
setState(() {
_isLoading = true;
});
try {
final result = await _apiService.fetchPosts(_currentPage);
setState(() {
_currentPage++;
_posts.addAll(result['posts']);
_hasMore = result['nextPageUrl'] != null;
});
} catch (e) {
throw Exception(e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: _buildPostList(),
floatingActionButton: FloatingActionButton(
onPressed: () {
//TODO action to add and edit post
},
child: const Icon(Icons.add),
),
);
}
Widget _buildPostList() {
return ListView.builder(
controller: _scrollController,
itemCount: _posts.length + (_hasMore ? 1 : 0),
itemBuilder: (context, index) {
if (index == _posts.length) {
return const Center(child: CircularProgressIndicator());
}
final post = _posts[index];
return Padding(
padding: const EdgeInsets.all(8.0),
child: Card.filled(
clipBehavior: Clip.hardEdge,
child: InkWell(
onTap: () {
// Todo action ke detail post
},
child: Stack(
children: [
PostView(post: post),
Positioned(
top: 2,
right: 2,
child: IconButton(
onPressed: () {
// TODO action to edit post
},
icon: const Icon(Icons.edit_rounded),
),
),
Positioned(
bottom: 2,
right: 2,
child: IconButton(
onPressed: () {
//TODO action delete post
},
icon: const Icon(Icons.delete_rounded),
),
)
],
),
),
),
);
},
);
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
}
Berikut adalah penjelasan kode yang diberikan:
Import Packages
import 'package:flutter/material.dart';
import 'package:flutter_pemula/api/repository.dart';
import 'package:flutter_pemula/component/post_view.dart';
import 'package:flutter_pemula/model/post.dart';
flutter/material.dart
: Mengimpor paket Flutter dasar untuk membangun antarmuka pengguna (UI).flutter_pemula/api/repository.dart
: Mengimpor filerepository.dart
yang berisi kelas untuk mengambil data dari API.flutter_pemula/component/post_view.dart
: Mengimpor filepost_view.dart
yang berisi widget untuk menampilkan post.flutter_pemula/model/post.dart
: Mengimpor filepost.dart
yang berisi model dataPost
.
Home Widget
class Home extends StatefulWidget {
final String title;
const Home({
super.key,
required this.title,
});
@override
State<Home> createState() => _HomeState();
}
Home
adalahStatefulWidget
yang menerimatitle
sebagai parameter.State<Home>
adalah state yang terkait dengan widgetHome
.
_HomeState
class _HomeState extends State<Home> {
final ScrollController _scrollController = ScrollController();
final Repository _apiService = Repository();
final List<Post> _posts = [];
bool _isLoading = false;
bool _hasMore = true;
int _currentPage = 1;
_scrollController
: Untuk mengontrol scroll pada list view._apiService
: Instance dariRepository
untuk mengambil data dari API._posts
: List yang menyimpan post yang diambil dari API._isLoading
: Boolean yang menunjukkan apakah sedang mengambil data._hasMore
: Boolean yang menunjukkan apakah masih ada data yang bisa diambil._currentPage
: Menyimpan nomor halaman saat ini untuk paginasi.
initState
@override
void initState() {
super.initState();
_loadPosts();
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
_loadMorePosts();
}
});
}
initState
: Dipanggil sekali saat widget dibuat. Mengambil data post awal dan menambahkan listener untuk mendeteksi ketika mencapai bagian bawah list untuk mengambil lebih banyak post.
_loadPosts dan _loadMorePosts
Future<void> _loadPosts() async {
setState(() {
_isLoading = true;
});
try {
final result = await _apiService.fetchPosts(_currentPage);
setState(() {
_currentPage++;
_posts.addAll(result['posts']);
_hasMore = result['nextPageUrl'] != null;
});
} catch (e) {
throw Exception(e.toString());
} finally {
setState(() {
_isLoading = false;
});
}
}
_loadPosts
: Mengambil post dari API, menambahkannya ke list_posts
, dan mengatur status loading._loadMorePosts
: Mirip dengan_loadPosts
, tapi hanya dipanggil ketika user scroll ke bawah.
build
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: _buildPostList(),
floatingActionButton: FloatingActionButton(
onPressed: () {
//TODO action to add and edit post
},
child: const Icon(Icons.add),
),
);
}
build
: Membuat UI denganAppBar
,ListView
untuk menampilkan post, danFloatingActionButton
untuk menambah atau mengedit post.
_buildPostList
Widget _buildPostList() {
return ListView.builder(
controller: _scrollController,
itemCount: _posts.length + (_hasMore ? 1 : 0),
itemBuilder: (context, index) {
if (index == _posts.length) {
return const Center(child: CircularProgressIndicator());
}
final post = _posts[index];
return Padding(
padding: const EdgeInsets.all(8.0),
child: Card.filled(
clipBehavior: Clip.hardEdge,
child: InkWell(
onTap: () {
// Todo action ke detail post
},
child: Stack(
children: [
PostView(post: post),
Positioned(
top: 2,
right: 2,
child: IconButton(
onPressed: () {
// TODO action to edit post
},
icon: const Icon(Icons.edit_rounded),
),
),
Positioned(
bottom: 2,
right: 2,
child: IconButton(
onPressed: () {
//TODO action delete post
},
icon: const Icon(Icons.delete_rounded),
),
)
],
),
),
),
);
},
);
}
_buildPostList
: MembuatListView
dengan itemCard
untuk setiap post. Jika masih ada data yang bisa diambil (_hasMore
), tambahkan indikator loading di bagian bawah.
dispose
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
dispose
: Membersihkan controller ketika widget dihapus untuk menghindari kebocoran memori.
Ini adalah penjelasan tentang kode yang diberikan, yang merupakan sebuah halaman Flutter untuk menampilkan daftar post yang diambil dari API dengan fitur paginasi dan scroll infinite.
Setelah kalian sudah membuat tampilan home atau tampilan pertama sekarang kita edit pada file main.dart
dan memanggil tampilan home
, sehingga kode pada main.dart
menjadi berikut:
import 'package:flutter/material.dart';
import 'package:flutter_pemula/screen/home.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const Home(title: 'Tutorial Flutter Pemula'),
);
}
}
setelah itu kalian run pada emulator atau device kalian masing- masing sehingga tampilannya menjadi berikut:
Kesimpulan
Pada artikel kali ini kita banyak belajar hal mulai dari membuat layout untuk menampilkan post dan mengetahui apa itu StatelessWidget
dan StatefulWidget
. untuk selanjutnya kita akan belajar cara menambah data dan mengirim gambar ke rest API.
Tutorial Flutter dengan Laravel Rest API #4: Insert dan Upload di Flutter dengan Rest API
Membangun Website dan Aplikasi Android Desa Dengan Laravel, React.js dan React Native
Membangun Aplikasi dan Website News Dengan Laravel, React.js dan Android