game html dan DIO

This commit is contained in:
='fauz 2025-07-07 17:31:26 +07:00
parent a211b0675d
commit 9bff9c7fc2
12 changed files with 157 additions and 23 deletions

1
.gitignore vendored
View File

@ -43,3 +43,4 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
pubspec.lock

View File

@ -224,19 +224,19 @@ figcaption {
<div class="div-grid">
<div class="div-item">
<img src="assets/html/furikake/nori.jpeg" alt="Nori">
<img src="nori.jpeg" alt="Nori">
<p>Nori (Rumput Laut)</p>
</div>
<div class="div-item">
<img src="assets/html/furikake/wijen.jpeg" alt="Biji Wijen">
<img src="wijen.jpeg" alt="Biji Wijen">
<p>Biji Wijen</p>
</div>
<div class="div-item">
<img src="assets/html/furikake/ikankering.jpg" alt="Katsuobushi">
<img src="ikankering.jpg" alt="Katsuobushi">
<p>Katsuobushi (Ikan Kering)</p>
</div>
<div class="div-item">
<img src="assets/html/furikake/gulagaram.jpg" alt="Garam dan Gula">
<img src="gulagaram.jpg" alt="Garam dan Gula">
<p>Garam & Gula</p>
</div>
</div>
@ -259,7 +259,7 @@ figcaption {
</ul>
<figure>
<img src="assets/html/furikake/furikake.jpg" alt="Contoh produk furikake" class="full-width-image">
<img src="furikake.jpg" alt="Contoh produk furikake" class="full-width-image">
<figcaption>Berbagai varian produk furikake yang tersedia di pasaran</figcaption>
</figure>
</section>

View File

@ -177,23 +177,23 @@
<div class="div-grid">
<div class="div-item">
<img src="assets/html/karbohidrat.png" alt="Karbohidrat" />
<img src="karbohidrat.png" alt="Karbohidrat" />
<p><strong>Karbohidrat</strong><br>Sumber energi seperti nasi, roti, dan kentang.</p>
</div>
<div class="div-item">
<img src="assets/html/protein.png" alt="Protein" />
<img src="protein.png" alt="Protein" />
<p><strong>Protein</strong><br>Untuk membangun otot, misalnya ikan, telur, dan tahu.</p>
</div>
<div class="div-item">
<img src="assets/html/lemak.png" alt="Lemak" />
<img src="lemak.png" alt="Lemak" />
<p><strong>Lemak</strong><br>Memberikan tenaga, seperti dari kacang-kacangan dan minyak sehat.</p>
</div>
<div class="div-item">
<img src="assets/html/vitaminmineral.png" alt="Vitamin dan Mineral" />
<img src="vitaminmineral.png" alt="Vitamin dan Mineral" />
<p><strong>Vitamin dan mineral</strong><br>Dari sayur dan buah yang membantu tubuh bekerja dengan baik.</p>
</div>
<div class="div-item">
<img src="assets/html/seratair.png" alt="Serat dan Air" />
<img src="seratair.png" alt="Serat dan Air" />
<p><strong>Serat dan air</strong><br>Membantu pencernaan dan menjaga tubuh tetap segar!</p>
</div>
</div>
@ -209,7 +209,7 @@
<li>🍽️ Seperempat piring: Protein (seperti ayam atau tempe)</li>
<li>🍽️ Sedikit lemak sehat</li>
</ul>
<img src="assets/html/isipiringku.png" alt="Piring Gizi Seimbang" class="full-width-image">
<img src="isipiringku.png" alt="Piring Gizi Seimbang" class="full-width-image">
<figcaption>Isi Piringku</figcaption>
<p>Selain itu, kita tidak boleh makan terlalu banyak atau terlalu sedikit, karena bisa membuat tubuh tidak sehat. Makan berlebihan bisa menyebabkan obesitas, sedangkan makan terlalu sedikit bisa membuat tubuh lemas dan kurang gizi.</p>
</section>

View File

@ -82,7 +82,7 @@
shuffled.forEach((img, i) => {
const card = document.createElement('div');
card.className = 'card hidden';
card.style.backgroundImage = 'url("freekake.png")';
card.style.backgroundImage = 'url("../freekake.png")';
card.style.backgroundSize = 'contain';
card.style.backgroundRepeat = 'no-repeat';
card.style.backgroundPosition = 'center';
@ -111,7 +111,7 @@
function hideCard(i) {
const el = board.children[i];
el.classList.add('hidden');
el.style.backgroundImage = 'url("freekake.png")';
el.style.backgroundImage = 'url("../freekake.png")';
el.style.backgroundSize = 'contain';
el.style.backgroundRepeat = 'no-repeat';
el.style.backgroundPosition = 'center';
@ -156,7 +156,6 @@
if (selected.length === 2) checkMatch();
});
// 🔍 Expose render and notify Flutter
window.render = render;
if (window.flutter_inappwebview) {

View File

@ -13,7 +13,7 @@ void main() async {
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown, // bisa dihapus jika hanya ingin satu arah
DeviceOrientation.portraitDown,
]);
runApp(const MyApp());

31
lib/models/users.dart Normal file
View File

@ -0,0 +1,31 @@
class Users {
int id;
// String nama;
// String tgl;
// String? alamat;
Users({
required this.id,
// required this.nama, this.alamat, required this.tgl
});
Map<String, dynamic> toMap() {
return {
"id": id,
// "nama": nama, "alamat": this.alamat, "tgl": this.tgl
};
}
factory Users.fromJson(Map<String, dynamic> map) {
return Users(
id: map['id'],
// nama: map['nama'],
// alamat: map['alamat'],
// tgl: map['tanggal'],
);
}
String toString() {
return 'Users{id:$id,}';
}
}

View File

@ -52,11 +52,13 @@ class _ListDetailScreenState extends State<ListDetailScreen> {
await controller.loadData(
data: html,
baseUrl: WebUri(
"http://localhost/$base",
"file:///android_asset/flutter_assets/$base",
), // agar path relatif seperti img1/* jalan
mimeType: 'text/html',
encoding: 'utf-8',
);
debugPrint("http://localhost/$base");
debugPrint("file:///android_asset/flutter_assets/$base");
},
onLoadStop: (controller, url) async {
debugPrint("🚀 onLoadStop called. URL: $url");

View File

@ -6,10 +6,13 @@ import 'package:flutter/services.dart';
import 'package:freekake/components/card_list.dart';
import 'package:freekake/components/collection_container%20copy.dart';
import 'package:freekake/helpers/color_helper.dart';
import 'package:freekake/models/users.dart';
import 'package:freekake/screen/pustaka/list_detail_screen.dart';
import 'package:freekake/util/api_client.dart';
import 'package:list_detail_extension/list_detail_extension.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:dio/dio.dart';
class ListEducation extends StatefulWidget {
final String? selectedCategory;
@ -27,6 +30,12 @@ class _ListEducationState extends State<ListEducation> {
String? selectedCategory;
final listDetailExtension = ListDetailExtension();
// Data fetch
final ApiClient _apiClient = ApiClient();
List<Users> _posts = [];
bool _isLoading = true;
String _error = '';
Future<void> _initExtension() async {
await listDetailExtension.load();
}
@ -165,6 +174,41 @@ class _ListEducationState extends State<ListEducation> {
});
}
Future<void> _fetchPosts() async {
try {
setState(() {
_isLoading = true;
_error = '';
});
// Melakukan permintaan GET ke endpoint
final Response response = await _apiClient.dio.get('auth/users');
if (response.statusCode == 200) {
// Konversi data JSON ke List
List<dynamic> postJson = response.data;
_posts = postJson.map((json) => Users.fromJson(json)).toList();
} else {
_error = 'Gagal memuat data: ${response.statusCode}';
}
} on DioException catch (e) {
if (e.response != null) {
_error =
'Server Error: ${e.response?.statusCode} - ${e.response?.statusMessage}';
} else {
_error = 'Network Error: ${e.message}';
}
print('DioError: $e');
} catch (e) {
_error = 'Terjadi kesalahan tidak terduga: $e';
print('General Error: $e');
} finally {
setState(() {
_isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
final List<Map<String, dynamic>> collections = [

View File

@ -328,13 +328,17 @@ class _PustakaScreenState extends State<PustakaScreen> {
Positioned(bottom: 0, left: 0, right: 0, child: MainMenu()),
],
),
Positioned(
bottom: bottomPadding * 98,
left: (screenWidth - buttonScanSize) / 2,
child: Transform.translate(
offset: Offset(0, -20),
child: ScanButton(),
),
Stack(
children: [
Positioned(
bottom: bottomPadding * 98,
left: (screenWidth - buttonScanSize) / 2,
child: Transform.translate(
offset: Offset(0, -20),
child: ScanButton(),
),
),
],
),
],
);

36
lib/util/api_client.dart Normal file
View File

@ -0,0 +1,36 @@
import 'package:dio/dio.dart';
class ApiClient {
final Dio _dio = Dio();
final String _baseUrl = 'https://api.freekake.com/';
ApiClient() {
_dio.options.baseUrl = _baseUrl;
_dio.options.connectTimeout = const Duration(seconds: 5);
_dio.options.receiveTimeout = const Duration(seconds: 3);
_dio.interceptors.add(
InterceptorsWrapper(
onRequest: (options, handler) {
print('REQUEST[${options.method}] => PATH: ${options.path}');
// options.headers['Authorization'] = 'Bearer your_token_here';
return handler.next(options);
},
onResponse: (response, handler) {
print(
'RESPONSE[${response.statusCode}] => PATH: ${response.requestOptions.path}',
);
return handler.next(response);
},
onError: (DioException e, handler) {
print(
'ERROR[${e.response?.statusCode}] => PATH: ${e.requestOptions.path}',
);
return handler.next(e);
},
),
);
}
Dio get dio => _dio;
}

View File

@ -129,6 +129,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.2.3"
dio:
dependency: "direct main"
description:
name: dio
sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9"
url: "https://pub.dev"
source: hosted
version: "5.8.0+1"
dio_web_adapter:
dependency: transitive
description:
name: dio_web_adapter
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
dropdown_button2:
dependency: "direct main"
description:

View File

@ -55,6 +55,7 @@ dependencies:
ref: main
desktop_webview_window: ^0.2.3
flutter_inappwebview: ^6.0.0
dio: ^5.0.0
# arcore_flutter_plugin: ^0.2.0-alpha
# flutter_unity_widget: ^2022.2.1
# arcore_flutter_plugin: