Compare commits

...

19 Commits

Author SHA1 Message Date
0e52ced016 Merge branch 'unity_embed' of https://git.probindo.com/Probindo/FreekakeApp into unity_embed 2025-05-07 13:39:04 +07:00
3ffa70486e pubspec 2025-05-07 13:34:39 +07:00
e6b81acaf3 updated 2025-05-07 13:34:13 +07:00
4fb5cbd567 mobile 2025-05-07 13:28:40 +07:00
8013646d0d mobile 2025-05-07 13:20:46 +07:00
66d72b9b1d Update README.md 2025-05-07 13:18:42 +07:00
17bfc6faee Update README.md 2025-05-07 13:18:42 +07:00
967cd86849 config gradle unity 2025-05-07 13:18:42 +07:00
9ae836b2a1 mobile 2025-05-07 13:18:42 +07:00
8b55983dd6 mobile 2025-05-07 13:17:44 +07:00
ffb4c5876c pubspec.lock 2025-05-07 13:17:44 +07:00
ff68b8b145 gitignore dan beberapa yang terlewat 2025-05-07 13:15:34 +07:00
e15bba626d config gradle unity 2025-05-07 13:15:34 +07:00
42642c2ef3 mobile 2025-05-07 13:15:34 +07:00
35860df873 mobile 2025-05-07 13:15:34 +07:00
='fauz
4dad153d28 update profile and html content 2025-05-05 18:11:34 +07:00
='fauz
5bfb381736 camera and bottom nav 2025-04-28 10:41:18 +07:00
='fauz
56d4697981 rotasi hilang 2025-04-22 15:17:57 +07:00
='fauz
1a2cf014b6 scan button and padding bottom scan button 2025-04-17 10:11:39 +07:00
36 changed files with 1474 additions and 468 deletions

View File

@ -43,6 +43,10 @@ android {
signingConfig = signingConfigs.getByName("debug") signingConfig = signingConfigs.getByName("debug")
} }
} }
dependencies {
implementation("androidx.core:core-ktx:1.9.0")
implementation("com.google.android.material:material:1.6.1")
}
} }
flutter { flutter {

View File

@ -11,7 +11,8 @@
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize"
android:screenOrientation="portrait">
<!-- Specifies an Android theme to apply to this Activity as soon as <!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues while the Flutter UI initializes. After that, this theme continues
@ -44,4 +45,6 @@
</queries> </queries>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
</manifest> </manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,279 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Furikake: Taburan Lezat untuk Nasi!</title>
<style>
/* Base Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
line-height: 1.6;
color: #333;
background-color: #fff;
padding: 0;
margin: 0;
font-size: 1rem;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
header {
margin-bottom: 30px;
border-bottom: 1px solid #eee;
padding-bottom: 20px;
}
main {
margin-bottom: 40px;
}
footer {
text-align: center;
padding: 20px 0;
border-top: 1px solid #eee;
font-size: 0.9rem;
color: #666;
}
h1 {
font-size: 2.2rem;
line-height: 1.2;
margin-bottom: 10px;
color: #222;
}
.subtitle {
font-size: 1.2rem;
color: #666;
font-weight: normal;
margin-bottom: 20px;
}
h2 {
font-size: 1.6rem;
margin: 30px 0 15px 0;
color: #333;
}
p {
margin-bottom: 20px;
font-size: 1.1rem;
line-height: 1.7;
}
ul {
margin-bottom: 20px;
padding-left: 20px;
}
li {
margin-bottom: 8px;
font-size: 1.1rem;
}
figure {
margin: 30px 0;
text-align: center;
}
img {
max-width: 100%;
height: auto;
border-radius: 4px;
}
.bahan-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 15px;
margin: 20px 0;
}
.bahan-item {
text-align: center;
flex: 1 0 calc(25% - 15px); /* 4 items per row by default */
min-width: calc(25% - 15px);
max-width: calc(25% - 15px);
}
.bahan-item img {
width: 100%;
height: 120px;
object-fit: cover;
border-radius: 8px;
}
.bahan-item p {
margin-top: 8px;
font-size: 0.9em;
}
.full-width-image {
width: 100%;
height: auto;
display: block;
margin: 0;
padding: 0;
}
figcaption {
font-size: 0.9rem;
color: #666;
margin-top: 10px;
}
/* Responsive Adjustments */
/* Tablet Size (768px and below) - 2 items per row */
@media (max-width: 768px) {
.bahan-item {
flex: 1 0 calc(50% - 15px);
min-width: calc(50% - 15px);
max-width: calc(50% - 15px);
}
}
/* Mobile Size (480px and below) - 1 item per row */
@media (max-width: 480px) {
h1 {
font-size: 1.8rem;
}
.japanese-title {
font-size: 2rem;
}
.subtitle {
font-size: 1.1rem;
}
h2 {
font-size: 1.4rem;
}
p, li {
font-size: 1rem;
}
.container {
padding: 15px;
}
.bahan-item {
flex: 1 0 100%;
min-width: 100%;
max-width: 100%;
}
}
/* Small Mobile (360px and below) - Adjust font sizes further */
@media (max-width: 360px) {
h1 {
font-size: 1.6rem;
}
h2 {
font-size: 1.3rem;
}
.subtitle {
font-size: 1rem;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>Furikake: Taburan Lezat untuk Nasi!</h1>
<p class="subtitle">Tahukah kamu bahwa di Jepang ada bumbu tabur yang bisa membuat nasi menjadi lebih enak?</p>
</header>
<div class="japanese-title">ふりかけ</div>
<main>
<section>
<p>Kata "furikake" berasal dari bahasa Jepang, yaitu "furi" yang berarti menaburkan dan "kake" yang berarti menuangkan. Jadi, furikake adalah bumbu yang ditaburkan di atas nasi supaya lebih lezat!</p>
</section>
<section>
<h2>Asal Usul Furikake</h2>
<p>Furikake pertama kali dibuat di Jepang pada tahun 1913 oleh seorang apoteker bernama Suekichi Yoshimaru. Awalnya, ia ingin membantu orang-orang yang kekurangan kalsium. Maka, ia membuat bubuk dari tulang ikan kering, biji wijen, dan rumput laut. Campuran ini kemudian diberi nama "Gohan no Tomo" yang artinya "Teman Nasi" karena memang cocok untuk dimakan bersama nasi.</p>
</section>
<section>
<h2>Perkembangan Furikake</h2>
<p>Seiring berjalannya waktu, furikake menjadi semakin populer dan dibuat dengan berbagai rasa. Dulu, furikake hanya bisa dibeli oleh orang kaya, tetapi setelah Perang Dunia II, furikake mulai dijual dengan harga terjangkau sehingga semua orang bisa menikmatinya. Akhirnya, pada tahun 1959, furikake semakin terkenal dan banyak orang mulai membuat berbagai variasi rasa yang lebih menarik.</p>
</section>
<section>
<h2>Apa Saja Isi Furikake?</h2>
<p>Furikake biasanya terbuat dari campuran beberapa bahan seperti:</p>
<div class="bahan-container">
<div class="bahan-item">
<img src="assets/html/furikake/nori.jpeg" alt="Nori">
<p>Nori (Rumput Laut)</p>
</div>
<div class="bahan-item">
<img src="assets/html/furikake/wijen.jpeg" alt="Biji Wijen">
<p>Biji Wijen</p>
</div>
<div class="bahan-item">
<img src="assets/html/furikake/ikankering.jpg" alt="Katsuobushi">
<p>Katsuobushi (Ikan Kering)</p>
</div>
<div class="bahan-item">
<img src="assets/html/furikake/gulagaram.jpg" alt="Garam dan Gula">
<p>Garam & Gula</p>
</div>
</div>
<ul>
<li>Rumput laut kering (nori) yang memberikan rasa laut yang khas.</li>
<li>Biji wijen yang menambah rasa gurih dan tekstur renyah.</li>
<li>Katsuobushi (ikan kering) yang menambah rasa gurih dan protein.</li>
<li>Gula dan garam untuk memberikan keseimbangan rasa.</li>
</ul>
</section>
<section>
<h2>Beragam Rasa Furikake</h2>
<p>Sekarang, furikake punya banyak rasa yang bisa dipilih sesuai selera! Beberapa contoh rasa furikake adalah:</p>
<ul>
<li>Wasabi Furikake, untuk yang suka rasa pedas khas wasabi.</li>
<li>Salmon Furikake, dengan aroma salmon yang lezat.</li>
<li>Furikake Vegan, yang dibuat tanpa bahan dari hewan, cocok untuk vegetarian.</li>
</ul>
<figure>
<img src="furikake.jpg" alt="Contoh produk furikake" class="full-width-image">
<figcaption>Berbagai varian produk furikake yang tersedia di pasaran</figcaption>
</figure>
</section>
<section>
<h2>Cara Menikmati Furikake</h2>
<p>Furikake sangat mudah digunakan! Cukup taburkan di atas nasi hangat, dan nasi pun menjadi lebih lezat. Bisa juga ditambahkan ke dalam mie, sup, atau onigiri (nasi kepal) untuk menambah rasa.</p>
<p>Nah, sekarang kamu tahu tentang furikake! Kalau suatu saat berkesempatan mencobanya, jangan lupa untuk menikmati kelezatannya. Siapa tahu, kamu jadi semakin suka makan nasi!</p>
</section>
</main>
<footer>
<p>Artikel tentang Furikake - Taburan Lezat untuk Nasi</p>
</footer>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

243
assets/html/index.html Normal file
View File

@ -0,0 +1,243 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Makan Sehat dengan Gizi Seimbang!</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
line-height: 1.6;
color: #333;
background-color: #fff;
padding: 0;
margin: 0;
font-size: 1rem;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
header {
margin-bottom: 30px;
border-bottom: 1px solid #eee;
padding-bottom: 20px;
}
main {
margin-bottom: 40px;
}
footer {
text-align: center;
padding: 20px 0;
border-top: 1px solid #eee;
font-size: 0.9rem;
color: #666;
}
h1 {
font-size: 2.2rem;
line-height: 1.2;
margin-bottom: 10px;
color: #222;
}
.subtitle {
font-size: 1.2rem;
color: #666;
font-weight: normal;
margin-bottom: 20px;
}
h2 {
font-size: 1.6rem;
margin: 30px 0 15px 0;
color: #333;
}
p {
margin-bottom: 20px;
font-size: 1.1rem;
line-height: 1.7;
}
ul {
margin-bottom: 20px;
padding-left: 20px;
}
li {
margin-bottom: 8px;
font-size: 1.1rem;
}
figure {
margin: 30px 0;
text-align: center;
}
img {
max-width: 100%;
height: auto;
border-radius: 4px;
}
.gizi-grid {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 5px;
margin-top: 5px;
}
.gizi-item {
text-align: center;
max-width: 100px;
flex: 1 1 200px;
}
.gizi-item img {
width: 100%;
height: auto;
border-radius: 10px;
}
.full-width-image {
width: 100%;
height: auto;
display: block;
margin: 0;
padding: 0;
}
figcaption {
font-size: 0.9rem;
text-align: center;
color: #666;
margin-top: 10px;
}
@media (max-width: 600px) {
h1 {
font-size: 1.8rem;
}
.subtitle {
font-size: 1.1rem;
}
h2 {
font-size: 1.4rem;
}
p, li {
font-size: 1rem;
}
.container {
padding: 15px;
}
.gizi-grid {
flex-direction: column;
align-items: center;
}
.gizi-item {
max-width: 80%;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>Makan Sehat dengan Gizi Seimbang!</h1>
<p class="subtitle">Tahukah kamu bahwa makan dengan gizi seimbang itu penting untuk tubuh kita?</p>
</header>
<main>
<section>
<p>Gizi seimbang berarti kita harus makan berbagai jenis makanan dengan jumlah yang pas supaya tubuh tetap sehat dan kuat!</p>
</section>
<section>
<h2>Apa Itu Gizi Seimbang?</h2>
<p>Gizi seimbang adalah cara makan yang mengandung semua zat gizi yang dibutuhkan tubuh. Zat gizi itu terdiri dari:</p>
<div class="gizi-grid">
<div class="gizi-item">
<img src="assets/html/karbohidrat.png" alt="Karbohidrat" />
<p><strong>Karbohidrat</strong><br>Sumber energi seperti nasi, roti, dan kentang.</p>
</div>
<div class="gizi-item">
<img src="assets/html/protein.png" alt="Protein" />
<p><strong>Protein</strong><br>Untuk membangun otot, misalnya ikan, telur, dan tahu.</p>
</div>
<div class="gizi-item">
<img src="assets/html/lemak.png" alt="Lemak" />
<p><strong>Lemak</strong><br>Memberikan tenaga, seperti dari kacang-kacangan dan minyak sehat.</p>
</div>
<div class="gizi-item">
<img src="assets/html/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="gizi-item">
<img src="assets/html/seratair.png" alt="Serat dan Air" />
<p><strong>Serat dan air</strong><br>Membantu pencernaan dan menjaga tubuh tetap segar!</p>
</div>
</div>
</section>
<section>
<h2>Porsi Makan yang Seimbang</h2>
<p>Supaya makan kita sehat, ada aturan "Isi Piringku" yang bisa kita ikuti:</p>
<ul>
<li>🍽️ Separuh piring: Sayur dan buah</li>
<li>🍽️ Seperempat piring: Karbohidrat (seperti nasi atau roti)</li>
<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">
<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>
<section>
<h2>Menjaga Kebersihan Makanan</h2>
<p>Selain memilih makanan sehat, kita juga harus menjaga kebersihan makanan agar tidak sakit perut! Caranya:</p>
<ul>
<li>Cuci tangan sebelum makan dan mengolah makanan.</li>
<li>Simpan makanan dengan baik agar tidak terkena kuman.</li>
<li>Masak makanan dengan suhu yang cukup supaya aman dimakan.</li>
</ul>
</section>
<section>
<h2>Aktivitas Fisik Itu Penting!</h2>
<p>Selain makan sehat, kita juga harus rajin bergerak! Olahraga atau bermain di luar rumah bisa membantu tubuh tetap bugar, menjaga berat badan, dan membuat kita lebih bersemangat.</p>
</section>
<section>
<h2>Yuk, Mulai Gizi Seimbang!</h2>
<p>Makan dengan gizi seimbang akan membuat tubuh kita lebih sehat, kuat, dan penuh energi. Yuk, biasakan makan sehat mulai sekarang supaya kita tumbuh dengan baik dan terhindar dari penyakit!</p>
</section>
</main>
<footer>
<p>Artikel tentang Makan Sehat dengan Gizi Seimbang</p>
</footer>
</div>
</body>
</html>

BIN
assets/html/isipiringku.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

BIN
assets/html/karbohidrat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

BIN
assets/html/lemak.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

BIN
assets/html/protein.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

BIN
assets/html/seratair.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

View File

@ -1,7 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class CardList extends StatelessWidget { class CardList extends StatelessWidget {
const CardList({super.key}); final String title;
final String body;
const CardList({super.key, required this.title, required this.body});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -19,7 +22,7 @@ class CardList extends StatelessWidget {
10.0, 10.0,
), // Opsional: membuat gambar lebih rounded ), // Opsional: membuat gambar lebih rounded
child: Image.asset( child: Image.asset(
'assets/images/cepott.png', // Ganti dengan path gambar Anda 'assets/images/cepott.png',
width: 80, // Atur ukuran gambar width: 80, // Atur ukuran gambar
height: 120, height: 120,
fit: BoxFit.cover, fit: BoxFit.cover,
@ -31,17 +34,18 @@ class CardList extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Judul Card", this.title,
style: const TextStyle( style: const TextStyle(
fontSize: 16, fontSize: 14,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
const SizedBox(height: 5, width: 10), const SizedBox(height: 1, width: 5),
Text( Text(
"Deskripsi singkat tentang item ini. karena ini sangat panjang maka ini akan dipotong", body,
style: const TextStyle( style: const TextStyle(
fontSize: 12, letterSpacing: 0.2,
fontSize: 14,
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
maxLines: 2, maxLines: 2,
@ -49,7 +53,7 @@ class CardList extends StatelessWidget {
), ),
const Spacer(), const Spacer(),
Padding( Padding(
padding: const EdgeInsets.all(8.0), padding: EdgeInsets.all(0.08),
child: const Row( child: const Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,

View File

@ -2,12 +2,17 @@ import 'package:flutter/material.dart';
import 'package:freekake/components/bottom_navbar.dart'; import 'package:freekake/components/bottom_navbar.dart';
import 'package:freekake/components/menu_button.dart'; import 'package:freekake/components/menu_button.dart';
import 'package:freekake/components/scan_button.dart'; import 'package:freekake/components/scan_button.dart';
import 'package:freekake/providers/menu_selection_provider.dart';
import 'package:freekake/screen/Home_screen.dart'; import 'package:freekake/screen/Home_screen.dart';
import 'package:freekake/screen/koleksi_screen.dart'; import 'package:freekake/screen/koleksi_screen.dart';
import 'package:freekake/screen/pustaka_screen.dart'; import 'package:freekake/screen/pustaka_screen.dart';
import 'package:freekake/screen/saya/profile_screen.dart'; import 'package:freekake/screen/saya/profile_screen.dart';
import 'package:provider/provider.dart'; import 'package:freekake/components/bottom_navbar.dart';
import 'package:freekake/components/menu_button.dart';
import 'package:freekake/components/scan_button.dart';
import 'package:freekake/screen/Home_screen.dart';
import 'package:freekake/screen/koleksi_screen.dart';
import 'package:freekake/screen/pustaka_screen.dart';
import 'package:freekake/screen/saya/profile_screen.dart';
class MainMenu extends StatefulWidget { class MainMenu extends StatefulWidget {
const MainMenu({super.key}); const MainMenu({super.key});
@ -44,7 +49,7 @@ class _MainMenuState extends State<MainMenu> {
Provider.of<MenuSelectionProvider>(context).selectedIndex; Provider.of<MenuSelectionProvider>(context).selectedIndex;
return Container( return Container(
color: Colors.transparent, color: Colors.transparent,
padding: const EdgeInsets.symmetric(vertical: 10), padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 5),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@ -52,27 +57,47 @@ class _MainMenuState extends State<MainMenu> {
MenuButton( MenuButton(
label: "E-furibuddy", label: "E-furibuddy",
icon: 'assets/icons/furrybuddy.svg', icon: 'assets/icons/furrybuddy.svg',
onPress: () => _onItemTapped(context, 0), onPress:
isSelected: selectedIndex == 0, () => {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HomeScreen()),
),
},
), ),
MenuButton( MenuButton(
label: "Koleksi", label: "Koleksi",
icon: 'assets/icons/Koleksi.svg', icon: 'assets/icons/Koleksi.svg',
onPress: () => _onItemTapped(context, 1), onPress:
isSelected: selectedIndex == 1, () => {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => KoleksiScreen()),
),
},
), ),
SizedBox(width: 100), SizedBox(width: 100),
MenuButton( MenuButton(
label: "Pustaka", label: "Pustaka",
icon: 'assets/icons/Pustaka.svg', icon: 'assets/icons/Pustaka.svg',
onPress: () => _onItemTapped(context, 2), onPress:
isSelected: selectedIndex == 2, () => {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PustakaScreen()),
),
},
), ),
MenuButton( MenuButton(
label: "Saya", label: "Saya",
icon: 'assets/icons/Saya.svg', icon: 'assets/icons/Saya.svg',
onPress: () => _onItemTapped(context, 3), onPress:
isSelected: selectedIndex == 3, () => {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ProfileScreen()),
),
},
), ),
], ],
), ),

View File

@ -17,7 +17,7 @@ class BNBCustomPainter3 extends CustomPainter {
path.quadraticBezierTo(size.width * 0.40, 0, size.width * 0.40, 20); path.quadraticBezierTo(size.width * 0.40, 0, size.width * 0.40, 20);
path.arcToPoint( path.arcToPoint(
Offset(size.width * 0.60, 20), Offset(size.width * 0.60, 20),
radius: Radius.circular(20.0), radius: Radius.circular(size.height * 0.5),
clockwise: false, clockwise: false,
); );
path.quadraticBezierTo(size.width * 0.60, 0, size.width * 0.63, 0); path.quadraticBezierTo(size.width * 0.60, 0, size.width * 0.63, 0);

View File

@ -1,9 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:freekake/components/navbar_container.dart';
import 'package:freekake/providers/character_provider.dart'; import 'package:freekake/providers/character_provider.dart';
import 'package:freekake/providers/menu_selection_provider.dart'; import 'package:freekake/providers/menu_selection_provider.dart';
import 'package:freekake/screen/Home_screen.dart'; import 'package:freekake/screen/Home_screen.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:furibase/providers/menu_selection_provider.dart';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();

View File

@ -255,6 +255,7 @@ class _HomeScreenState extends State<HomeScreen> {
], ],
), ),
Positioned(bottom: 0, left: 0, right: 0, child: MainMenu()), Positioned(bottom: 0, left: 0, right: 0, child: MainMenu()),
Positioned( Positioned(
// bottom: -150, //bottomPadding * 32, // bottom: -150, //bottomPadding * 32,
// bottom: sunRiseCurve( // bottom: sunRiseCurve(

View File

@ -0,0 +1,99 @@
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
class CameraScreen extends StatefulWidget {
@override
_CameraScreenState createState() => _CameraScreenState();
}
class _CameraScreenState extends State<CameraScreen> {
CameraController? controller;
List<CameraDescription>? cameras;
XFile? capturedImage;
@override
void initState() {
super.initState();
initCamera();
}
Future<void> initCamera() async {
await Permission.camera.request();
if (await Permission.camera.isGranted) {
cameras = await availableCameras();
controller = CameraController(cameras![0], ResolutionPreset.high);
await controller!.initialize();
setState(() {}); // update UI setelah kamera siap
}
}
@override
void dispose() {
controller?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (controller == null || !controller!.value.isInitialized) {
return Center(child: CircularProgressIndicator());
}
return Scaffold(
appBar: AppBar(
title: Text('Camera'),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
Navigator.pop(context);
},
),
),
body: Stack(
children: [
CameraPreview(controller!),
if (capturedImage != null)
Positioned.fill(
child: Image.file(File(capturedImage!.path), fit: BoxFit.cover),
),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.only(bottom: 30),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FloatingActionButton(
backgroundColor: Colors.white,
onPressed: () async {
final image = await controller!.takePicture();
setState(() {
capturedImage = image;
});
Navigator.pop(context, capturedImage?.path);
},
child: Icon(Icons.camera_alt, color: Colors.black),
),
],
),
),
),
],
),
// floatingActionButton: FloatingActionButton(
// onPressed: () async {
// final image = await controller!.takePicture();
// print('Gambar disimpan di: ${image.path}');
// // Bisa dilanjutkan untuk upload, crop, dll
// },
// child: Icon(Icons.camera_alt),
// ),
);
}
}

View File

@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_html/flutter_html.dart';
class CharacterView extends StatefulWidget {
final String title;
final String imagePath;
final String content;
const CharacterView({
Key? key,
required this.title,
required this.imagePath,
required this.content,
}) : super(key: key);
@override
_CharacterViewState createState() => _CharacterViewState();
}
class _CharacterViewState extends State<CharacterView> {
String _content = '';
@override
void initState() {
super.initState();
_content = widget.content;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text(widget.title),
backgroundColor: Color.fromARGB(225, 79, 76, 182),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 10,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(40),
bottomRight: Radius.circular(40),
),
color: Color.fromARGB(225, 79, 76, 182),
),
),
),
const SizedBox(height: 0),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.only(left: 18.0, right: 18, bottom: 18),
child: Text(_content),
),
),
],
),
// ),
);
}
}

View File

@ -36,6 +36,9 @@ class _KoleksiScreenState extends State<KoleksiScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
final buttonScanSize = screenWidth * 0.20;
final double bottomPadding = (9000 / screenWidth).clamp(0, 0.2);
return Scaffold( return Scaffold(
extendBody: true, extendBody: true,
body: Stack( body: Stack(
@ -150,8 +153,8 @@ class _KoleksiScreenState extends State<KoleksiScreen> {
), ),
Positioned(bottom: 0, left: 0, right: 0, child: MainMenu()), Positioned(bottom: 0, left: 0, right: 0, child: MainMenu()),
Positioned( Positioned(
bottom: 8, bottom: bottomPadding * 98,
left: MediaQuery.of(context).size.width / 2 - 38, left: (screenWidth - buttonScanSize) / 2,
child: Transform.translate( child: Transform.translate(
offset: Offset(0, -20), offset: Offset(0, -20),
child: ScanButton(), child: ScanButton(),

View File

@ -1,9 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:webview_flutter/webview_flutter.dart';
class ListDetailScreen extends StatefulWidget { class ListDetailScreen extends StatefulWidget {
final String title; final String title;
final String? filepath;
const ListDetailScreen({super.key, required this.title}); const ListDetailScreen({super.key, required this.title, this.filepath});
@override @override
State<ListDetailScreen> createState() => _ListDetailScreenState(); State<ListDetailScreen> createState() => _ListDetailScreenState();
@ -11,6 +14,8 @@ class ListDetailScreen extends StatefulWidget {
class _ListDetailScreenState extends State<ListDetailScreen> { class _ListDetailScreenState extends State<ListDetailScreen> {
List<bool> checklist = []; List<bool> checklist = [];
late WebViewController _controller;
late String? fPath = widget.filepath;
@override @override
void initState() { void initState() {
@ -25,12 +30,26 @@ class _ListDetailScreenState extends State<ListDetailScreen> {
"Paragraf keempat yang melengkapi pembahasan.", "Paragraf keempat yang melengkapi pembahasan.",
]; ];
void _loadContent(String path) async {
if (path.startsWith('http')) {
// Load dari Internet
_controller.loadRequest(Uri.parse(path));
} else {
// Load dari assets lokal
String fileHtml = await rootBundle.loadString(path);
_controller.loadHtmlString(fileHtml);
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: Colors.white, backgroundColor: Colors.white,
appBar: AppBar(title: Text(widget.title)), appBar: AppBar(title: Text(widget.title)),
body: Padding( body:
(fPath != null && fPath!.isNotEmpty)
? WebViewWidget(controller: _controller)
: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Column( child: Column(
children: [ children: [

View File

@ -1,11 +1,15 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:freekake/components/buildcard_info.dart'; import 'package:freekake/components/buildcard_info.dart';
import 'package:freekake/components/card_list.dart'; import 'package:freekake/components/card_list.dart';
import 'package:freekake/components/collection_container%20copy.dart'; import 'package:freekake/components/collection_container%20copy.dart';
import 'package:freekake/components/main_menu.dart'; import 'package:freekake/components/main_menu.dart';
import 'package:freekake/components/topbar_container.dart'; import 'package:freekake/components/topbar_container.dart';
import 'package:freekake/screen/pustaka/pustaka_detail_screen.dart';
import 'package:freekake/helpers/color_helper.dart'; import 'package:freekake/helpers/color_helper.dart';
import 'package:freekake/screen/pustaka/pustaka_detail_screen.dart';
class ListEducation extends StatefulWidget { class ListEducation extends StatefulWidget {
const ListEducation({super.key}); const ListEducation({super.key});
@ -16,6 +20,17 @@ class ListEducation extends StatefulWidget {
class _ListEducationState extends State<ListEducation> { class _ListEducationState extends State<ListEducation> {
TextEditingController searchController = TextEditingController(); TextEditingController searchController = TextEditingController();
late final WebViewController _controller;
late String urlOrAssetPath;
bool _shouldShowWebView = false;
// load extension
final listDetailExtension = ListDetailExtension();
Future<void> _initExtension() async {
await listDetailExtension.load();
}
List<String> allItems = [ List<String> allItems = [
"Matematika", "Matematika",
"Fisika", "Fisika",
@ -24,20 +39,83 @@ class _ListEducationState extends State<ListEducation> {
"Sejarah", "Sejarah",
"Geografi", "Geografi",
]; ];
List<String> filteredItems = [];
final List<Map<String, dynamic>> _listContent = [
{
"title": "Taburan Lezat untuk Nasi!",
"image": "assets/icons/healthy.svg",
"color": "#cdd0ee",
"category": "Kesehatan",
"body":
"""Tahukah kamu bahwa di Jepang ada bumbu tabur yang bisa membuat nasi
menjadi lebih enak?
""",
"file": "assets/html/furikake/index.html",
},
{
"title": "Makan Sehat dengan Gizi Seimbang",
"image": "assets/icons/Safety.svg",
"color": "#cef1da",
"category": "Keselamatan",
"body":
"""upaya makan kita sehat, ada aturan "Isi Piringku" yang bisa kita ikuti:
🍽 Separuh piring: Sayur dan buah 🍽 Seperempat piring: Karbohidrat (seperti nasi atau roti)
🍽 Seperempat piring: Protein (seperti ayam atau tempe) 🍽 Sedikit lemak sehat
""",
"file": "assets/html/index.html",
},
];
List<Map<String, dynamic>> filteredItems = [];
@override @override
void initState() { void initState() {
super.initState(); super.initState();
filteredItems = List.from(allItems); filteredItems = List.from(_listContent);
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
_shouldShowWebView = true;
_controller =
WebViewController()..setJavaScriptMode(JavaScriptMode.unrestricted);
urlOrAssetPath = "assets/makanan_bergizi/index.html";
_loadContent(urlOrAssetPath);
_initExtension();
}
}
void _loadContent(String path) async {
if (_shouldShowWebView && _controller != null) {
if (path.startsWith('http')) {
// Load dari Internet
_controller.loadRequest(Uri.parse(path));
} else {
// Load dari assets lokal
// String fileHtml = await rootBundle.loadString(path);
String fileHtml = await DefaultAssetBundle.of(
context,
).loadString('assets/html/index.html');
_controller.loadHtmlString(fileHtml);
}
}
} }
void filterSearch(String query) { void filterSearch(String query) {
setState(() { setState(() {
if (query.isEmpty) {
filteredItems = List.from(_listContent);
} else {
filteredItems = filteredItems =
allItems _listContent.where((item) {
.where((item) => item.toLowerCase().contains(query.toLowerCase())) return item["title"].toLowerCase().contains(query.toLowerCase());
.toList(); }).toList();
}
});
}
void filterByCategory(String category) {
setState(() {
filteredItems =
_listContent.where((item) {
return item["category"].toLowerCase() == category.toLowerCase();
}).toList();
}); });
} }
@ -70,14 +148,16 @@ class _ListEducationState extends State<ListEducation> {
"category": "keselamatan", "category": "keselamatan",
}, },
]; ];
List<Map<String, dynamic>> filteredCollections =
final List<Map<String, dynamic>> filteredCollections =
_collections _collections
.where( .where(
(item) => item["label"]!.toLowerCase().contains( (item) => item["label"].toLowerCase().contains(
_searchQuery.toLowerCase(), _searchQuery.toLowerCase(),
), ),
) )
.toList(); .toList();
if (_shouldShowWebView && _controller != null) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Color.fromARGB(225, 79, 76, 182), backgroundColor: Color.fromARGB(225, 79, 76, 182),
@ -168,11 +248,13 @@ class _ListEducationState extends State<ListEducation> {
height: 70, height: 70,
textColor: Colors.black, textColor: Colors.black,
lblSize: 10, lblSize: 10,
colorContiner: ColorHelper.hexToColor(item["color"]), colorContiner: ColorHelper.hexToColor(
item["color"],
),
onTapAc: onTapAc:
() => { () => {
searchController.clear(), searchController.clear(),
filterSearch(item["category"]), filterByCategory(item["category"]),
}, },
); );
}).toList(), }).toList(),
@ -180,56 +262,12 @@ class _ListEducationState extends State<ListEducation> {
], ],
), ),
), ),
// Search Bar
// Padding(
// padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
// child: Container(
// height: 47,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(20),
// color: Colors.grey.shade200,
// ),
// padding: const EdgeInsets.symmetric(horizontal: 10),
// child: Row(
// children: [
// const Icon(
// Icons.search,
// color: Color.fromARGB(137, 180, 172, 172),
// size: 18,
// ),
// const SizedBox(width: 8),
// Expanded(
// child: TextField(
// controller: searchController,
// onChanged: filterSearch, // Memanggil filter saat mengetik
// style: const TextStyle(color: Colors.black, fontSize: 14),
// decoration: const InputDecoration(
// border: InputBorder.none,
// hintText: 'Cari pelajaran...',
// hintStyle: TextStyle(color: Colors.black54),
// ),
// ),
// ),
// if (searchController.text.isNotEmpty)
// IconButton(
// icon: const Icon(Icons.close, color: Colors.black54),
// onPressed: () {
// searchController.clear();
// filterSearch('');
// },
// ),
// ],
// ),
// ),
// ),
// Collection Items dengan Filter // Collection Items dengan Filter
SizedBox(height: 20), SizedBox(height: 20),
Expanded( Expanded(
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color.fromARGB(224, 145, 145, 155), color: const Color.fromARGB(223, 200, 200, 206),
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
topLeft: Radius.circular(25), topLeft: Radius.circular(25),
topRight: Radius.circular(25), topRight: Radius.circular(25),
@ -243,11 +281,10 @@ class _ListEducationState extends State<ListEducation> {
horizontal: MediaQuery.of(context).size.width / 4, horizontal: MediaQuery.of(context).size.width / 4,
), ),
child: Divider( child: Divider(
color: const Color.fromARGB(255, 155, 147, 147), color: const Color.fromARGB(255, 255, 255, 255),
height: 20, thickness: 2,
), ),
), ),
Expanded( Expanded(
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
@ -267,9 +304,9 @@ class _ListEducationState extends State<ListEducation> {
) )
: Padding( : Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
top: 20.0, top: 10.0,
left: 20, left: 10,
right: 20, right: 10,
), ),
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
@ -298,10 +335,17 @@ class _ListEducationState extends State<ListEducation> {
mainAxisSpacing: 10, mainAxisSpacing: 10,
childAspectRatio: 2.5, childAspectRatio: 2.5,
), ),
itemCount: filteredItems.length, itemCount: _listContent.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return GestureDetector( return GestureDetector(
child: CardList(), child: CardList(
title:
_listContent[index]["title"] ??
"",
body:
_listContent[index]["body"] ??
"",
),
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
@ -309,28 +353,18 @@ class _ListEducationState extends State<ListEducation> {
builder: builder:
( (
context, context,
) => ListDetailScreen( ) => listDetailExtension.buildPage({
title: 'title':
filteredItems[index], filteredItems[index]['category'],
imagePath: 'imagePath':
filteredItems[index], filteredItems[index]['image'],
paragraphs: [ 'filepath':
filteredItems[index]['file'],
'paragraphs':
""" """
<h1>Enter the main heading, usually the same as the title.</h1> """ +
<p>Be <b>bold</b> in stating your key points. Put them in a list: </p> "<h1> ${_listContent[index]["title"]} </h1> <p> ${_listContent[index]["body"]}",
<ul> }),
<li>The first item in your list</li>
<li>The second item; <i>italicize</i> key words</li>
</ul>
<p>Improve your image by including an image. </p>
<p><img src="https://via.placeholder.com/150" alt="A Great HTML Resource"></p>
<p>Add a link to your favorite <a href="https://www.dummies.com/">Web site</a>.</p>
<hr>
<p>Finally, link to <a href="page2.html">another page</a> in your own Web site.</p>
<p>© Wiley Publishing, 2011</p>
""",
],
),
), ),
); );
}, },
@ -346,10 +380,16 @@ class _ListEducationState extends State<ListEducation> {
), ),
), ),
), ),
// Bottom Navigation
// MainMenu(),
], ],
), ),
); );
} else {
return const Center(
child: Padding(
padding: EdgeInsets.all(40),
child: Text("WebView tidak tersedia untuk platform ini."),
),
);
}
} }
} }

View File

@ -1,16 +1,19 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_html/flutter_html.dart';
class ListDetailScreen extends StatefulWidget { class ListDetailScreen extends StatefulWidget {
final String title; final String title;
final String imagePath; final String imagePath;
final List<String> paragraphs; final String paragraphs;
final String filepath;
const ListDetailScreen({ const ListDetailScreen({
Key? key, Key? key,
required this.title, required this.title,
required this.imagePath, required this.imagePath,
required this.paragraphs, required this.paragraphs,
required this.filepath,
}) : super(key: key); }) : super(key: key);
@override @override
@ -19,11 +22,13 @@ class ListDetailScreen extends StatefulWidget {
class _ListDetailScreenState extends State<ListDetailScreen> { class _ListDetailScreenState extends State<ListDetailScreen> {
late List<bool> _readStatus; late List<bool> _readStatus;
String _htmlContent = '';
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_readStatus = List.generate(widget.paragraphs.length, (index) => false); _readStatus = List.generate(widget.paragraphs.length, (index) => false);
loadHtml();
} }
void _toggleReadStatus(int index) { void _toggleReadStatus(int index) {
@ -32,6 +37,15 @@ class _ListDetailScreenState extends State<ListDetailScreen> {
}); });
} }
Future<void> loadHtml() async {
String html = await DefaultAssetBundle.of(
context,
).loadString(widget.filepath);
setState(() {
_htmlContent = html;
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -55,96 +69,148 @@ class _ListDetailScreenState extends State<ListDetailScreen> {
), ),
), ),
), ),
Padding(
padding: const EdgeInsets.all(20.0), // Padding(
child: Row( // padding: const EdgeInsets.all(20.0),
crossAxisAlignment: CrossAxisAlignment.start, // child: Row(
children: [ // crossAxisAlignment: CrossAxisAlignment.start,
Image.asset( // children: [
widget.imagePath, // Image.asset(
width: 100, // widget.imagePath,
height: 100, // width: 100,
fit: BoxFit.cover, // height: 100,
), // fit: BoxFit.cover,
const SizedBox(width: 16),
Expanded(
child: Text(
widget.title,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
),
],
),
),
const SizedBox(height: 16),
Expanded(
child: Padding(
padding: const EdgeInsets.all(18.0),
child: ListView.builder(
itemCount: widget.paragraphs.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child:
// Text(
// widget.paragraphs[index] +
// style: TextStyle(color: Colors.black),
Html(
style: {"body": Style(color: Colors.black)},
data:
widget.paragraphs.toString() +
"""
<html>
<!-- Text between angle brackets is an HTML tag and is not displayed.
Most tags, such as the HTML and /HTML tags that surround the contents of
a page, come in pairs; some tags, like HR, for a horizontal rule, stand
alone. Comments, such as the text you're reading, are not displayed when
the Web page is shown. The information between the HEAD and /HEAD tags is
not displayed. The information between the BODY and /BODY tags is displayed.-->
<head>
<title>Enter a title, displayed at the top of the window.</title>
</head>
<!-- The information between the BODY and /BODY tags is displayed.-->
<body>
<h1>Enter the main heading, usually the same as the title.</h1>
<p>Be <b>bold</b> in stating your key points. Put them in a list: </p>
<ul>
<li>The first item in your list</li>
<li>The second item; <i>italicize</i> key words</li>
</ul>
<p>Improve your image by including an image. </p>
<p><img src="http://www.mygifs.com/CoverImage.gif" alt="A Great HTML Resource"></p>
<p>Add a link to your favorite <a href="https://www.dummies.com/">Web site</a>.
Break up your page with a horizontal rule or two. </p>
<hr>
<p>Finally, link to <a href="page2.html">another page</a> in your own Web site.</p>
<!-- And add a copyright notice.-->
<p>© Wiley Publishing, 2011</p>
</body>
</html>
""",
),
),
// ), // ),
// Checkbox( // const SizedBox(width: 16),
// value: _readStatus[index], // Expanded(
// onChanged: (bool? value) { // child: Center(
// _toggleReadStatus(index); // child: Text(
// widget.title,
// style: const TextStyle(
// fontSize: 22,
// fontWeight: FontWeight.bold,
// color: Colors.black,
// ),
// ),
// ),
// ),
// ],
// ),
// ),
const SizedBox(height: 0),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.only(left: 18.0, right: 18, bottom: 18),
child:
// ListView.builder(
// itemCount: widget.paragraphs.length,
// itemBuilder: (context, index) {
// return
// Padding(
// padding: const EdgeInsets.only(bottom: 16.0),
// child: Row(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Expanded(
// child:
// // Text(
// // widget.paragraphs[index] +
// // style: TextStyle(color: Colors.black),
// Html(
// // style: {
// // "body": Style(
// // color: const Color.fromRGBO(0, 0, 0, 1),
// // ),
// // },
// data:
// widget.paragraphs +
// """
// <html>
// <!-- Text between angle brackets is an HTML tag and is not displayed.
// Most tags, such as the HTML and /HTML tags that surround the contents of
// a page, come in pairs; some tags, like HR, for a horizontal rule, stand
// alone. Comments, such as the text you're reading, are not displayed when
// the Web page is shown. The information between the HEAD and /HEAD tags is
// not displayed. The information between the BODY and /BODY tags is displayed.-->
// <head>
// <title>Enter a title, displayed at the top of the window.</title>
// </head>
// <!-- The information between the BODY and /BODY tags is displayed.-->
// <body>
// <h1>Enter the main heading, usually the same as the title.</h1>
// <p>Be <b>bold</b> in stating your key points. Put them in a list: </p>
// <ul>
// <li>The first item in your list</li>
// <li>The second item; <i>italicize</i> key words</li>
// </ul>
// <p>Improve your image by including an image. </p>
// <p><img src="http://www.mygifs.com/CoverImage.gif" alt="A Great HTML Resource"></p>
// <p>Add a link to your favorite <a href="https://www.dummies.com/">Web site</a>.
// Break up your page with a horizontal rule or two. </p>
// <hr>
// <p>Finally, link to <a href="page2.html">another page</a> in your own Web site.</p>
// <!-- And add a copyright notice.-->
// <p>© Wiley Publishing, 2011</p>
// </body>
// </html>
// """,
// ),
// ),
// // ),
// // Checkbox(
// // value: _readStatus[index],
// // onChanged: (bool? value) {
// // _toggleReadStatus(index);
// // },
// // ),
// ],
// ),
// );
// }, // },
// ), // ),
], Html(
), style: {"body": Style(color: const Color.fromRGBO(0, 0, 0, 1))},
// data: widget.paragraphs,
data: _htmlContent,
extensions: [
TagExtension(
tagsToExtend: {"img"},
builder: (ExtensionContext context) {
final attrs = context.attributes;
final src = attrs['src'] ?? '';
if (src.isNotEmpty) {
if (src.startsWith('assets/html/')) {
return Image.asset(
src,
width: 100,
errorBuilder:
(context, error, stackTrace) =>
const Text('Image not found'),
); );
} else if (!src.startsWith('http')) {
return Image.asset(
'assets/images/$src',
width: 100,
errorBuilder:
(context, error, stackTrace) =>
const Text('Image not found'),
);
} else {
return Image.network(
src,
width: 100,
errorBuilder:
(context, error, stackTrace) =>
const Text('Image failed to load'),
);
}
}
return const SizedBox();
}, },
), ),
],
),
), ),
), ),
], ],

View File

@ -49,6 +49,9 @@ class _PustakaScreenState extends State<PustakaScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
final buttonScanSize = screenWidth * 0.20;
final double bottomPadding = (9000 / screenWidth).clamp(0, 0.2);
List<Map<String, dynamic>> filteredCollections = List<Map<String, dynamic>> filteredCollections =
_collections _collections
.where( .where(
@ -284,8 +287,8 @@ class _PustakaScreenState extends State<PustakaScreen> {
], ],
), ),
Positioned( Positioned(
bottom: 8, bottom: bottomPadding * 98,
left: MediaQuery.of(context).size.width / 2 - 38, left: (screenWidth - buttonScanSize) / 2,
child: Transform.translate( child: Transform.translate(
offset: Offset(0, -20), offset: Offset(0, -20),
child: ScanButton(), child: ScanButton(),

View File

@ -17,6 +17,8 @@ class AkunSaya extends StatefulWidget {
class _AkunSayaState extends State<AkunSaya> { class _AkunSayaState extends State<AkunSaya> {
dynamic _profileImage; dynamic _profileImage;
dynamic _headerImage; dynamic _headerImage;
ImageProvider? imageProvider;
final ImagePicker _picker = ImagePicker(); final ImagePicker _picker = ImagePicker();
Future<void> _pickImage(bool isProfile) async { Future<void> _pickImage(bool isProfile) async {
@ -34,13 +36,19 @@ class _AkunSayaState extends State<AkunSaya> {
} else { } else {
final pickedFile = await _picker.pickImage(source: ImageSource.gallery); final pickedFile = await _picker.pickImage(source: ImageSource.gallery);
if (pickedFile != null) { if (pickedFile != null) {
final imageFile = File(pickedFile.path);
setState(() { setState(() {
if (isProfile) { if (isProfile) {
_profileImage = File(pickedFile.path); _profileImage = imageFile;
} else { } else {
_headerImage = File(pickedFile.path); _headerImage = imageFile;
} }
imageProvider =
kIsWeb ? MemoryImage(_profileImage) : FileImage(_profileImage);
}); });
} else {
imageProvider = AssetImage("assets/images/luffy.png");
} }
} }
} }
@ -96,7 +104,7 @@ class _AkunSayaState extends State<AkunSaya> {
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 40), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 40),
children: [ children: [
TextFormField( TextFormField(
initialValue: "Cepot", initialValue: "Luffy Kun",
decoration: const InputDecoration( decoration: const InputDecoration(
border: UnderlineInputBorder(), border: UnderlineInputBorder(),
labelText: 'Nama', labelText: 'Nama',
@ -105,7 +113,7 @@ class _AkunSayaState extends State<AkunSaya> {
style: TextStyle(color: Colors.black), style: TextStyle(color: Colors.black),
), ),
TextFormField( TextFormField(
initialValue: "cpt09", initialValue: "luffy01",
decoration: const InputDecoration( decoration: const InputDecoration(
border: UnderlineInputBorder(), border: UnderlineInputBorder(),
labelText: 'Nama Pengguna', labelText: 'Nama Pengguna',
@ -168,7 +176,7 @@ class _AkunSayaState extends State<AkunSaya> {
: null, : null,
child: child:
_profileImage == null _profileImage == null
? Icon(Icons.person, size: 50, color: Colors.white) ? Image.asset("assets/images/luffy.png")
: null, : null,
), ),
CircleAvatar( CircleAvatar(
@ -183,14 +191,14 @@ class _AkunSayaState extends State<AkunSaya> {
Positioned( Positioned(
top: 130, top: 130,
left: MediaQuery.of(context).size.width / 2 - 50, left: MediaQuery.of(context).size.width / 2 - 30,
child: GestureDetector( child: GestureDetector(
onTap: () => _pickImage(true), onTap: () => _pickImage(true),
child: Stack( child: Stack(
alignment: Alignment.bottomRight, alignment: Alignment.bottomRight,
children: [ children: [
Text( Text(
"Nama Akun", "Luffy kun",
style: TextStyle( style: TextStyle(
color: Colors.deepPurple, color: Colors.deepPurple,
fontSize: 18, fontSize: 18,

View File

@ -23,6 +23,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
// final ImagePicker _picker = ImagePicker(); // final ImagePicker _picker = ImagePicker();
dynamic _profileImage; dynamic _profileImage;
dynamic _headerImage; dynamic _headerImage;
ImageProvider? imageProvider;
final ImagePicker _picker = ImagePicker(); final ImagePicker _picker = ImagePicker();
Future<void> _pickImage(bool isProfile) async { Future<void> _pickImage(bool isProfile) async {
@ -40,19 +41,28 @@ class _ProfileScreenState extends State<ProfileScreen> {
} else { } else {
final pickedFile = await _picker.pickImage(source: ImageSource.gallery); final pickedFile = await _picker.pickImage(source: ImageSource.gallery);
if (pickedFile != null) { if (pickedFile != null) {
final imageFile = File(pickedFile.path);
setState(() { setState(() {
if (isProfile) { if (isProfile) {
_profileImage = File(pickedFile.path); _profileImage = imageFile;
} else { } else {
_headerImage = File(pickedFile.path); _headerImage = imageFile;
} }
imageProvider =
kIsWeb ? MemoryImage(_profileImage) : FileImage(_profileImage);
}); });
} else {
imageProvider = AssetImage("assets/images/luffy.png");
} }
} }
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
final buttonScanSize = screenWidth * 0.20;
final double bottomPadding = (9000 / screenWidth).clamp(0, 0.2);
return Scaffold( return Scaffold(
backgroundColor: const Color.fromARGB(255, 255, 255, 255), backgroundColor: const Color.fromARGB(255, 255, 255, 255),
body: Stack( body: Stack(
@ -131,10 +141,8 @@ class _ProfileScreenState extends State<ProfileScreen> {
: null, : null,
child: child:
_profileImage == null _profileImage == null
? Icon( ? Image.asset(
Icons.person, "assets/images/luffy.png",
size: 50,
color: Colors.white,
) )
: null, : null,
), ),
@ -158,9 +166,16 @@ class _ProfileScreenState extends State<ProfileScreen> {
Row( Row(
children: [ children: [
Text( Text(
"Name", "Luffy",
style: TextStyle( style: TextStyle(
color: Colors.blueAccent, color: const Color.fromARGB(
255,
0,
0,
0,
),
fontSize: 16,
fontWeight: FontWeight.bold,
), ),
), ),
], ],
@ -168,9 +183,15 @@ class _ProfileScreenState extends State<ProfileScreen> {
Row( Row(
children: [ children: [
Text( Text(
"Edit Profile", "Luffy Kun",
style: TextStyle( style: TextStyle(
color: Colors.blueAccent, color: const Color.fromARGB(
255,
0,
0,
0,
),
fontWeight: FontWeight.normal,
), ),
), ),
], ],
@ -340,8 +361,8 @@ class _ProfileScreenState extends State<ProfileScreen> {
), ),
Positioned(bottom: 0, left: 0, right: 0, child: MainMenu()), Positioned(bottom: 0, left: 0, right: 0, child: MainMenu()),
Positioned( Positioned(
bottom: 8, bottom: bottomPadding * 98,
left: MediaQuery.of(context).size.width / 2 - 38, left: (screenWidth - buttonScanSize) / 2,
child: Transform.translate( child: Transform.translate(
offset: Offset(0, -20), offset: Offset(0, -20),
child: ScanButton(), child: ScanButton(),

View File

@ -6,10 +6,14 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <desktop_webview_window/desktop_webview_window_plugin.h>
#include <file_selector_linux/file_selector_plugin.h> #include <file_selector_linux/file_selector_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h> #include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) desktop_webview_window_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewWindowPlugin");
desktop_webview_window_plugin_register_with_registrar(desktop_webview_window_registrar);
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar); file_selector_plugin_register_with_registrar(file_selector_linux_registrar);

View File

@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
desktop_webview_window
file_selector_linux file_selector_linux
url_launcher_linux url_launcher_linux
) )

View File

@ -5,12 +5,14 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import desktop_webview_window
import file_selector_macos import file_selector_macos
import shared_preferences_foundation import shared_preferences_foundation
import url_launcher_macos import url_launcher_macos
import webview_flutter_wkwebview import webview_flutter_wkwebview
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DesktopWebviewWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewWindowPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))

View File

@ -33,6 +33,46 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.2" version: "2.1.2"
camera:
dependency: "direct main"
description:
name: camera
sha256: dfa8fc5a1adaeb95e7a54d86a5bd56f4bb0e035515354c8ac6d262e35cec2ec8
url: "https://pub.dev"
source: hosted
version: "0.10.6"
camera_android:
dependency: transitive
description:
name: camera_android
sha256: bd9737671a00d979e0310a946e5be2fdc621b6a36b95378755cb3e4498e61485
url: "https://pub.dev"
source: hosted
version: "0.10.10+2"
camera_avfoundation:
dependency: transitive
description:
name: camera_avfoundation
sha256: a33cd9a250296271cdf556891b7c0986a93772426f286595eccd5f45b185933c
url: "https://pub.dev"
source: hosted
version: "0.9.18+14"
camera_platform_interface:
dependency: transitive
description:
name: camera_platform_interface
sha256: "2f757024a48696ff4814a789b0bd90f5660c0fb25f393ab4564fb483327930e2"
url: "https://pub.dev"
source: hosted
version: "2.10.0"
camera_web:
dependency: transitive
description:
name: camera_web
sha256: "595f28c89d1fb62d77c73c633193755b781c6d2e0ebcd8dc25b763b514e6ba8f"
url: "https://pub.dev"
source: hosted
version: "0.3.5"
characters: characters:
dependency: transitive dependency: transitive
description: description:
@ -81,6 +121,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.8" version: "1.0.8"
desktop_webview_window:
dependency: "direct main"
description:
name: desktop_webview_window
sha256: "57cf20d81689d5cbb1adfd0017e96b669398a669d927906073b0e42fc64111c0"
url: "https://pub.dev"
source: hosted
version: "0.2.3"
dropdown_button2: dropdown_button2:
dependency: "direct main" dependency: "direct main"
description: description:
@ -210,7 +258,7 @@ packages:
source: hosted source: hosted
version: "0.15.4" version: "0.15.4"
http: http:
dependency: transitive dependency: "direct main"
description: description:
name: http name: http
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
@ -337,6 +385,15 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.2" version: "1.0.2"
list_detail_extension:
dependency: "direct main"
description:
path: "."
ref: main
resolved-ref: e80f292f5a1a72de280fded5385c549507e12e0e
url: "https://github.com/fhauze/list_detail_screen.git"
source: git
version: "0.0.1"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
@ -425,6 +482,54 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.0" version: "2.3.0"
permission_handler:
dependency: "direct main"
description:
name: permission_handler
sha256: "59adad729136f01ea9e35a48f5d1395e25cba6cea552249ddbe9cf950f5d7849"
url: "https://pub.dev"
source: hosted
version: "11.4.0"
permission_handler_android:
dependency: transitive
description:
name: permission_handler_android
sha256: d3971dcdd76182a0c198c096b5db2f0884b0d4196723d21a866fc4cdea057ebc
url: "https://pub.dev"
source: hosted
version: "12.1.0"
permission_handler_apple:
dependency: transitive
description:
name: permission_handler_apple
sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023
url: "https://pub.dev"
source: hosted
version: "9.4.7"
permission_handler_html:
dependency: transitive
description:
name: permission_handler_html
sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24"
url: "https://pub.dev"
source: hosted
version: "0.1.3+5"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878
url: "https://pub.dev"
source: hosted
version: "4.3.0"
permission_handler_windows:
dependency: transitive
description:
name: permission_handler_windows
sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
url: "https://pub.dev"
source: hosted
version: "0.2.1"
petitparser: petitparser:
dependency: transitive dependency: transitive
description: description:
@ -695,45 +800,37 @@ packages:
source: hosted source: hosted
version: "1.1.0" version: "1.1.0"
webview_flutter: webview_flutter:
dependency: transitive dependency: "direct main"
description: description:
name: webview_flutter name: webview_flutter
sha256: "889a0a678e7c793c308c68739996227c9661590605e70b1f6cf6b9a6634f7aec" sha256: caf0f5a1012aa3c2d33c4215adc72dc1194bb59a2d3ed901f457965626805e66
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.10.0" version: "4.11.0"
webview_flutter_android: webview_flutter_android:
dependency: transitive dependency: "direct main"
description: description:
name: webview_flutter_android name: webview_flutter_android
sha256: "512c26ccc5b8a571fd5d13ec994b7509f142ff6faf85835e243dde3538fdc713" sha256: "5c3b6f992d123084903ec091b84f021c413a92a9af49038e4564a1b26c8452cf"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.3.2" version: "4.4.1"
webview_flutter_platform_interface: webview_flutter_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_platform_interface name: webview_flutter_platform_interface
sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d sha256: "18b1640839cf6546784a524c72aded5b6e86b23e7167dc2311cc96f7658b64bd"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.10.0" version: "2.11.0"
webview_flutter_web:
dependency: transitive
description:
name: webview_flutter_web
sha256: "18a7ccc1c31dd9a5c759a1b7217a2a1e04bd8f65712714a4070bfac19a23ca9e"
url: "https://pub.dev"
source: hosted
version: "0.2.3+4"
webview_flutter_wkwebview: webview_flutter_wkwebview:
dependency: transitive dependency: "direct main"
description: description:
name: webview_flutter_wkwebview name: webview_flutter_wkwebview
sha256: d7403ef4f042714c9ee2b26eaac4cadae7394cb0d4e608b1dd850c3ff96bd893 sha256: c9f9be526fa0d3347374ceaa05c4b3acb85f4f112abd62f7d74b7d301fa515ff
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.18.2" version: "3.20.0"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:

View File

@ -30,7 +30,9 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
camera: ^0.10.5+5
http: 1.3.0
permission_handler: ^11.3.0
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8 cupertino_icons: ^1.0.8
@ -44,6 +46,14 @@ dependencies:
image_picker_for_web: ^3.0.6 image_picker_for_web: ^3.0.6
image_picker_web: ^4.0.0 image_picker_web: ^4.0.0
flutter_plugin_android_lifecycle: ^2.0.9 flutter_plugin_android_lifecycle: ^2.0.9
webview_flutter: ^4.11.0
webview_flutter_android: ^4.4.1
webview_flutter_wkwebview: ^3.9.3
list_detail_extension:
git:
url: https://github.com/fhauze/list_detail_screen.git
ref: main
desktop_webview_window: ^0.2.3
# arcore_flutter_plugin: ^0.2.0-alpha # arcore_flutter_plugin: ^0.2.0-alpha
# flutter_unity_widget: ^2022.2.1 # flutter_unity_widget: ^2022.2.1
# arcore_flutter_plugin: # arcore_flutter_plugin:

View File

@ -6,12 +6,18 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <desktop_webview_window/desktop_webview_window_plugin.h>
#include <file_selector_windows/file_selector_windows.h> #include <file_selector_windows/file_selector_windows.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h> #include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
DesktopWebviewWindowPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("DesktopWebviewWindowPlugin"));
FileSelectorWindowsRegisterWithRegistrar( FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows")); registry->GetRegistrarForPlugin("FileSelectorWindows"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar( UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows")); registry->GetRegistrarForPlugin("UrlLauncherWindows"));
} }

View File

@ -3,7 +3,9 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
desktop_webview_window
file_selector_windows file_selector_windows
permission_handler_windows
url_launcher_windows url_launcher_windows
) )