Compare commits

..
67 Commits
Author SHA1 Message Date
irwan.l.cahyono c7105d6110 Merge branch 'unity_embed' of https://git.probindo.com/Probindo/FreekakeApp into unity_embed 2025-05-10 23:14:31 +07:00
irwan.l.cahyono e15bfc0bef gradle config 2025-05-10 23:14:18 +07:00
irwan.l.cahyono 442e56294f pubspec 2025-05-10 23:14:18 +07:00
irwan.l.cahyono 2b7778b152 sebelum unity 2025-05-10 23:14:18 +07:00
irwan.l.cahyono d9b5e53e18 pubspec.lock 2025-05-10 23:11:03 +07:00
irwan.l.cahyono 740f137913 mobile 2025-05-10 23:10:12 +07:00
irwan.l.cahyono 89dbf1a900 mobile 2025-05-10 22:49:55 +07:00
irwan.l.cahyono 9899809c18 config gradle unity 2025-05-10 22:38:40 +07:00
irwan.l.cahyono 4040e8f91a mobile 2025-05-10 22:38:40 +07:00
irwan.l.cahyono fe2d68e786 mobile 2025-05-10 22:38:06 +07:00
irwan.l.cahyono f7ed9f5035 pubspec.lock 2025-05-10 22:37:32 +07:00
irwan.l.cahyono 7cea1ea4c5 mobile 2025-05-10 22:36:22 +07:00
irwan.l.cahyono 501d417b95 mobile 2025-05-10 22:33:16 +07:00
irwan.l.cahyono db213c151c config gradle unity 2025-05-10 22:24:42 +07:00
irwan.l.cahyono 72aa4f9dfe mobile 2025-05-10 22:24:42 +07:00
irwan.l.cahyono 7946df58d8 mobile 2025-05-10 22:24:22 +07:00
irwan.l.cahyono c08aeb999c mobile 2025-05-10 22:17:35 +07:00
irwan.l.cahyono 206f99b561 main 2025-05-10 22:08:29 +07:00
irwan.l.cahyono e7945b1074 updated 2025-05-10 22:08:29 +07:00
irwan.l.cahyono dbafded5d2 mobile 2025-05-10 22:08:29 +07:00
irwan.l.cahyono fe66f466a2 mobile 2025-05-10 22:08:29 +07:00
irwan.l.cahyono 06fe0bdd7e config gradle unity 2025-05-10 22:08:29 +07:00
irwan.l.cahyono 00749179f6 mobile 2025-05-10 22:08:29 +07:00
irwan.l.cahyono 15e85fc0dd mobile 2025-05-10 22:08:29 +07:00
irwan.l.cahyono 52df106632 mobile 2025-05-10 22:08:29 +07:00
irwan.l.cahyono da4f3f9243 pubspec 2025-05-10 22:08:29 +07:00
irwan.l.cahyono 5f8305289c updated 2025-05-10 22:08:29 +07:00
irwan.l.cahyono ecdad52570 Update README.md 2025-05-10 22:04:54 +07:00
irwan.l.cahyono 1bd2a67868 Update README.md 2025-05-10 22:04:54 +07:00
irwan.l.cahyono fa47208e4e config gradle unity 2025-05-10 22:04:54 +07:00
irwan.l.cahyono 41e0248b67 mobile 2025-05-10 22:04:54 +07:00
irwan.l.cahyono b330c207b7 mobile 2025-05-10 22:04:54 +07:00
irwan.l.cahyono 17e955e3a9 pubspec.lock 2025-05-10 22:04:54 +07:00
irwan.l.cahyono ce76938284 gitignore dan beberapa yang terlewat 2025-05-10 22:04:54 +07:00
irwan.l.cahyono 316d1937b4 config gradle unity 2025-05-10 22:04:54 +07:00
irwan.l.cahyono 85a2655457 mobile 2025-05-10 22:04:54 +07:00
='fauz a3c27a23b9 okey 2025-05-10 16:14:57 +07:00
='fauz 8478822f4a halaman baru 2025-05-10 14:48:08 +07:00
irwan.l.cahyono d2d45ad685 gradle config 2025-05-09 02:00:18 +07:00
irwan.l.cahyono 86b1f67dda pubspec 2025-05-09 01:59:25 +07:00
irwan.l.cahyono 0e985f5453 sebelum unity 2025-05-09 01:42:10 +07:00
irwan.l.cahyono 2a4f8fc335 Merge branch 'unity_embed' of https://git.probindo.com/Probindo/FreekakeApp into unity_embed 2025-05-08 21:41:45 +07:00
irwan.l.cahyono efcfb609d5 main 2025-05-08 20:09:55 +07:00
irwan.l.cahyono dba0e9d6cd updated 2025-05-08 20:09:55 +07:00
irwan.l.cahyono 93d15dd59f mobile 2025-05-08 20:08:04 +07:00
irwan.l.cahyono 0a8ef32fe4 mobile 2025-05-08 20:03:07 +07:00
irwan.l.cahyono 0b54a5126d config gradle unity 2025-05-08 19:57:44 +07:00
irwan.l.cahyono 927f805484 mobile 2025-05-08 19:57:18 +07:00
irwan.l.cahyono 5230d7dcca mobile 2025-05-08 19:53:37 +07:00
irwan.l.cahyono d05d5242db mobile 2025-05-08 19:52:38 +07:00
irwan.l.cahyono aa2d7a0194 pubspec 2025-05-08 19:45:50 +07:00
irwan.l.cahyono 340a66bfb2 updated 2025-05-08 19:45:38 +07:00
irwan.l.cahyono 467470ce90 Update README.md 2025-05-08 19:42:03 +07:00
irwan.l.cahyono 3f37619744 Update README.md 2025-05-08 19:42:03 +07:00
irwan.l.cahyono f57088b04a config gradle unity 2025-05-08 19:42:03 +07:00
irwan.l.cahyono 278750083e mobile 2025-05-08 19:42:03 +07:00
irwan.l.cahyono 854efa1986 mobile 2025-05-08 19:41:44 +07:00
irwan.l.cahyono af92616ef7 pubspec.lock 2025-05-08 19:40:46 +07:00
irwan.l.cahyono fa9c15f7c8 gitignore dan beberapa yang terlewat 2025-05-08 19:40:46 +07:00
irwan.l.cahyono ad3591b042 config gradle unity 2025-05-08 19:40:46 +07:00
irwan.l.cahyono c6bcf88caf mobile 2025-05-08 19:37:25 +07:00
irwan.l.cahyono 4e19c82046 main 2025-05-08 19:22:44 +07:00
='fauz d41e1f84a0 kepala dan icon 2025-05-08 13:32:33 +07:00
='fauz b771de5e80 kepala cepot 2025-05-08 13:18:37 +07:00
='fauz d9bedf51a9 update 01 2025-05-08 13:16:02 +07:00
='fauz ee9c2ce4a4 Modular menus 2025-05-07 14:18:26 +07:00
='fauz f47eb88980 freekake basename 2025-05-07 13:56:17 +07:00
49 changed files with 2342 additions and 1234 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ plugins {
}
android {
namespace = "com.example.furibase"
namespace = "com.paj.freekake"
//compileSdk = flutter.compileSdkVersion
compileSdk = 35
//ndkVersion = "27.0.12077973"
+1 -1
View File
@@ -1,6 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="furibase"
android:label="freekake"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
@@ -1,4 +1,4 @@
package com.example.furibase
package com.paj.freekake
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.android.FlutterFragmentActivity;
+2 -1
View File
@@ -1,5 +1,6 @@
#Thu May 08 22:45:23 WIB 2025
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
+12 -12
View File
@@ -93,7 +93,7 @@ img {
border-radius: 4px;
}
.bahan-container {
.div-grid {
display: flex;
flex-wrap: wrap;
justify-content: center;
@@ -101,21 +101,21 @@ img {
margin: 20px 0;
}
.bahan-item {
.div-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 {
.div-item img {
width: 100%;
height: 120px;
object-fit: cover;
border-radius: 8px;
}
.bahan-item p {
.div-item p {
margin-top: 8px;
font-size: 0.9em;
}
@@ -138,7 +138,7 @@ figcaption {
/* Tablet Size (768px and below) - 2 items per row */
@media (max-width: 768px) {
.bahan-item {
.div-item {
flex: 1 0 calc(50% - 15px);
min-width: calc(50% - 15px);
max-width: calc(50% - 15px);
@@ -171,7 +171,7 @@ figcaption {
padding: 15px;
}
.bahan-item {
.div-item {
flex: 1 0 100%;
min-width: 100%;
max-width: 100%;
@@ -222,20 +222,20 @@ figcaption {
<h2>Apa Saja Isi Furikake?</h2>
<p>Furikake biasanya terbuat dari campuran beberapa bahan seperti:</p>
<div class="bahan-container">
<div class="bahan-item">
<div class="div-grid">
<div class="div-item">
<img src="assets/html/furikake/nori.jpeg" alt="Nori">
<p>Nori (Rumput Laut)</p>
</div>
<div class="bahan-item">
<div class="div-item">
<img src="assets/html/furikake/wijen.jpeg" alt="Biji Wijen">
<p>Biji Wijen</p>
</div>
<div class="bahan-item">
<div class="div-item">
<img src="assets/html/furikake/ikankering.jpg" alt="Katsuobushi">
<p>Katsuobushi (Ikan Kering)</p>
</div>
<div class="bahan-item">
<div class="div-item">
<img src="assets/html/furikake/gulagaram.jpg" alt="Garam dan Gula">
<p>Garam & Gula</p>
</div>
@@ -259,7 +259,7 @@ figcaption {
</ul>
<figure>
<img src="furikake.jpg" alt="Contoh produk furikake" class="full-width-image">
<img src="assets/html/furikake/furikake.jpg" alt="Contoh produk furikake" class="full-width-image">
<figcaption>Berbagai varian produk furikake yang tersedia di pasaran</figcaption>
</figure>
</section>
+11 -11
View File
@@ -92,7 +92,7 @@
border-radius: 4px;
}
.gizi-grid {
.div-grid {
display: flex;
flex-wrap: wrap;
justify-content: center;
@@ -100,13 +100,13 @@
margin-top: 5px;
}
.gizi-item {
.div-item {
text-align: center;
max-width: 100px;
flex: 1 1 200px;
}
.gizi-item img {
.div-item img {
width: 100%;
height: auto;
border-radius: 10px;
@@ -148,12 +148,12 @@
padding: 15px;
}
.gizi-grid {
.div-grid {
flex-direction: column;
align-items: center;
}
.gizi-item {
.div-item {
max-width: 80%;
}
}
@@ -175,24 +175,24 @@
<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">
<div class="div-grid">
<div class="div-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">
<div class="div-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">
<div class="div-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">
<div class="div-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">
<div class="div-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>
Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

+1
View File
@@ -13,6 +13,7 @@ class BottomNavbar extends StatefulWidget {
}
class _BottomNavbarState extends State<BottomNavbar> {
@override
Widget build(BuildContext context) {
final Size screenSize = MediaQuery.of(context).size;
return CustomPaint(
+95 -22
View File
@@ -3,8 +3,20 @@ import 'package:flutter/material.dart';
class CardList extends StatelessWidget {
final String title;
final String body;
final String? gambar;
final String? tipe;
final String? point;
final String? coint;
const CardList({super.key, required this.title, required this.body});
const CardList({
super.key,
required this.title,
required this.body,
this.gambar,
this.tipe,
this.point,
this.coint,
});
@override
Widget build(BuildContext context) {
@@ -21,51 +33,112 @@ class CardList extends StatelessWidget {
borderRadius: BorderRadius.circular(
10.0,
), // Opsional: membuat gambar lebih rounded
child: Image.asset(
'assets/images/cepott.png',
width: 80, // Atur ukuran gambar
height: 120,
fit: BoxFit.cover,
),
child:
(gambar != null)
? Image.asset(
gambar!,
width: 65, // Atur ukuran gambar
height: 80,
fit: BoxFit.cover,
)
: Image.asset(
'assets/images/cepott.png',
width: 80, // Atur ukuran gambar
height: 120,
fit: BoxFit.cover,
),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 1, width: 5),
Text(
this.title,
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 1, width: 5),
Text(
body,
style: const TextStyle(
letterSpacing: 0.2,
fontSize: 14,
fontWeight: FontWeight.normal,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
// Text(
// body,
// style: const TextStyle(
// letterSpacing: 0.2,
// fontSize: 14,
// fontWeight: FontWeight.normal,
// ),
// maxLines: 2,
// overflow: TextOverflow.ellipsis,
// ),
const Spacer(),
Padding(
padding: EdgeInsets.all(0.08),
child: const Row(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Text("50"),
Chip(
labelPadding: EdgeInsets.symmetric(
horizontal: 0,
vertical: 0,
),
label: Text(
point ?? ' 200',
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
),
backgroundColor: Colors.blue,
avatar: const CircleAvatar(
backgroundColor: Colors.transparent,
child: Icon(
Icons.emoji_events,
size: 16,
color: Colors.amber,
),
),
),
SizedBox(width: 40),
Text("100"),
Chip(
labelPadding: EdgeInsets.symmetric(
horizontal: 0,
vertical: 0,
),
label: Text(
coint ?? " 100",
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
),
backgroundColor: Colors.blue,
avatar: const CircleAvatar(
backgroundColor: Colors.transparent,
child: Icon(
Icons.monetization_on,
size: 12,
color: Colors.amber,
),
),
),
],
),
Text("Quiz"),
Chip(
labelPadding: EdgeInsets.symmetric(
horizontal: 0,
vertical: 0,
),
label: Text(
tipe ?? "Text",
style: TextStyle(color: Colors.white, fontSize: 14),
),
backgroundColor: Colors.blue,
),
],
),
),
+142
View File
@@ -0,0 +1,142 @@
import 'package:flutter/material.dart';
class CharacterInfo extends StatelessWidget {
final String nama;
final String kelamin;
final String asal;
final String content;
const CharacterInfo({
super.key,
required this.nama,
required this.kelamin,
required this.asal,
required this.content,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
children: [
SizedBox(
width: 120,
child: Text(
"Nama Karakter",
style: const TextStyle(
letterSpacing: 0.2,
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
),
SizedBox(width: 5),
Text(
":",
style: const TextStyle(
letterSpacing: 0.2,
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
SizedBox(width: 5),
Text(
nama,
style: const TextStyle(
letterSpacing: 0.2,
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
],
),
Row(
children: [
SizedBox(
width: 120,
child: Text(
"Jenis Kelamin",
style: const TextStyle(
letterSpacing: 0.2,
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
),
SizedBox(width: 5),
Text(
":",
style: const TextStyle(
letterSpacing: 0.2,
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
SizedBox(width: 5),
Text(
kelamin,
style: const TextStyle(
letterSpacing: 0.2,
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
],
),
Row(
children: [
SizedBox(
width: 120,
child: Text(
"Asal Karakter",
style: const TextStyle(
letterSpacing: 0.2,
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
),
SizedBox(width: 5),
Text(
":",
style: const TextStyle(
letterSpacing: 0.2,
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
SizedBox(width: 5),
Text(
asal,
style: const TextStyle(
letterSpacing: 0.2,
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
],
),
SizedBox(height: 20),
Text(
content,
style: const TextStyle(
letterSpacing: 0.2,
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black,
),
),
],
);
}
}
+2 -2
View File
@@ -17,7 +17,7 @@ class CollectionContainer extends StatelessWidget {
final bool? small;
const CollectionContainer({
Key? key,
super.key,
this.label,
this.imagesrc,
this.imageSvg,
@@ -31,7 +31,7 @@ class CollectionContainer extends StatelessWidget {
this.circle,
this.colorContiner,
this.small,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {
+2 -1
View File
@@ -6,6 +6,7 @@ class CurvedBottomBorderTab extends StatelessWidget {
final VoidCallback tap;
const CurvedBottomBorderTab({
super.key,
required this.isSelected,
required this.label,
required this.tap,
@@ -16,6 +17,7 @@ class CurvedBottomBorderTab extends StatelessWidget {
return CustomPaint(
painter: isSelected ? CurvedBottomPainter() : null,
child: GestureDetector(
onTap: tap,
child: Container(
width: 110,
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
@@ -34,7 +36,6 @@ class CurvedBottomBorderTab extends StatelessWidget {
),
),
),
onTap: tap,
),
);
}
+10 -10
View File
@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:freekake/components/navbar_shape_2.dart';
// import 'package:freekake/components/navbar_shape_2.dart';
class CustomShape extends StatelessWidget {
const CustomShape({super.key});
@@ -23,7 +23,7 @@ class RPSCustomPainter extends CustomPainter {
void paint(Canvas canvas, Size size) {
// Layer 1
Paint paint_fill_0 =
Paint paintFill0 =
Paint()
..color = const Color.fromARGB(255, 189, 18, 18)
..style = PaintingStyle.fill
@@ -67,11 +67,11 @@ class RPSCustomPainter extends CustomPainter {
);
path_0.close();
canvas.drawPath(path_0, paint_fill_0);
canvas.drawPath(path_0, paintFill0);
// Layer 1
Paint paint_stroke_0 =
Paint paintStroke0 =
Paint()
..color = const Color.fromARGB(255, 33, 150, 243)
..style = PaintingStyle.stroke
@@ -79,11 +79,11 @@ class RPSCustomPainter extends CustomPainter {
..strokeCap = StrokeCap.butt
..strokeJoin = StrokeJoin.miter;
canvas.drawPath(path_0, paint_stroke_0);
canvas.drawPath(path_0, paintStroke0);
// Layer 1
Paint paint_fill_1 =
Paint paintFill1 =
Paint()
..color = const Color.fromARGB(255, 28, 163, 52)
..style = PaintingStyle.fill
@@ -116,11 +116,11 @@ class RPSCustomPainter extends CustomPainter {
);
path_1.close();
canvas.drawPath(path_1, paint_fill_1);
canvas.drawPath(path_1, paintFill1);
// Layer 1
Paint paint_stroke_1 =
Paint paintStroke1 =
Paint()
..color = const Color.fromARGB(255, 33, 150, 243)
..style = PaintingStyle.stroke
@@ -128,7 +128,7 @@ class RPSCustomPainter extends CustomPainter {
..strokeCap = StrokeCap.butt
..strokeJoin = StrokeJoin.miter;
canvas.drawPath(path_1, paint_stroke_1);
canvas.drawPath(path_1, paintStroke1);
// layer 2
// Paint paint_fill_0 =
@@ -164,7 +164,7 @@ class RPSCustomPainter extends CustomPainter {
);
path_0.close();
canvas.drawPath(path_0, paint_fill_0);
canvas.drawPath(path_0, paintFill0);
// Layer 1
+36 -71
View File
@@ -6,16 +6,10 @@ 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';
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 {
const MainMenu({super.key});
@override
_MainMenuState createState() => _MainMenuState();
}
@@ -29,19 +23,6 @@ class _MainMenuState extends State<MainMenu> {
ProfileScreen(),
];
// void _onItemTapped(int index) {
// Future.delayed(Duration(milliseconds: 100), () {
// Navigator.pushReplacement(
// context,
// MaterialPageRoute(builder: (context) => _screens[index]),
// );
// });
// setState(() {
// selectedIndex = index;
// });
// }
@override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
@@ -50,57 +31,41 @@ class _MainMenuState extends State<MainMenu> {
return Container(
color: Colors.transparent,
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.min,
children: [
MenuButton(
label: "E-furibuddy",
icon: 'assets/icons/furrybuddy.svg',
onPress:
() => {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HomeScreen()),
),
},
),
MenuButton(
label: "Koleksi",
icon: 'assets/icons/Koleksi.svg',
onPress:
() => {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => KoleksiScreen()),
),
},
),
SizedBox(width: 100),
MenuButton(
label: "Pustaka",
icon: 'assets/icons/Pustaka.svg',
onPress:
() => {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PustakaScreen()),
),
},
),
MenuButton(
label: "Saya",
icon: 'assets/icons/Saya.svg',
onPress:
() => {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ProfileScreen()),
),
},
),
],
),
child: _btns(context, selectedIndex),
);
}
Row _btns(BuildContext context, int selectedIndex) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.min,
children: [
MenuButton(
label: "E-furibuddy",
icon: 'assets/icons/furrybuddy.svg',
onPress: () => _onItemTapped(context, 0),
isSelected: selectedIndex == 0,
),
MenuButton(
label: "Koleksi",
icon: 'assets/icons/Koleksi.svg',
onPress: () => _onItemTapped(context, 1),
isSelected: selectedIndex == 1,
),
SizedBox(width: 100),
MenuButton(
label: "Pustaka",
icon: 'assets/icons/Pustaka.svg',
onPress: () => _onItemTapped(context, 2),
isSelected: selectedIndex == 2,
),
MenuButton(
label: "Saya",
icon: 'assets/icons/Saya.svg',
onPress: () => _onItemTapped(context, 3),
isSelected: selectedIndex == 3,
),
],
);
}
+7
View File
@@ -0,0 +1,7 @@
import 'package:flutter/material.dart';
class MenuItem {
const MenuItem({required this.text, required this.icon});
final String text;
final IconData icon;
}
+1 -2
View File
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:freekake/components/navbar_shape_2.dart';
class NavbarContainer extends StatelessWidget {
const NavbarContainer({super.key});
@@ -23,7 +22,7 @@ class MyShape extends CustomPainter {
void paint(Canvas canvas, Size size) {
// TODO: implement paint
Paint paint =
new Paint()
Paint()
..color = const Color.fromARGB(255, 166, 12, 66)
..style = PaintingStyle.fill;
+1 -1
View File
@@ -7,7 +7,7 @@ class BNBCustomPainter2 extends CustomPainter {
void paint(Canvas canvas, Size size) {
debugPrint("Drawing navbar...");
Paint paint =
new Paint()
Paint()
..color = const Color.fromARGB(255, 79, 76, 182)
..style = PaintingStyle.fill;
+1 -1
View File
@@ -7,7 +7,7 @@ class BNBCustomPainter3 extends CustomPainter {
void paint(Canvas canvas, Size size) {
debugPrint("Drawing navbar...");
Paint paint =
new Paint()
Paint()
..color = const Color.fromARGB(255, 79, 76, 182)
..style = PaintingStyle.fill;
+39
View File
@@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:freekake/providers/point_provider.dart';
import 'package:provider/provider.dart';
class PointsHeader extends StatelessWidget {
@override
Widget build(BuildContext context) {
final points = context.watch<PointsProvider>().points;
return Container(
padding: EdgeInsets.all(12),
color: const Color.fromARGB(99, 255, 255, 255),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
"Activity", //${points.xp}/${points.xpToNextLevel()}",
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
Text(
"",
// "🔥 Level: ${points.level}",
// style: TextStyle(color: Colors.orangeAccent),
),
Text(
"🏆",
// "💎 GP: ${points.gp}",
style: TextStyle(color: Colors.lightBlue),
),
Text(
"🟡",
// "🎯" SP: ${points.sp}",
style: TextStyle(color: Colors.greenAccent),
),
],
),
);
}
}
+2 -1
View File
@@ -1,10 +1,11 @@
import 'package:freekake/screen/camera_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:freekake/screen/camera_screen.dart';
class ScanButton extends StatelessWidget {
const ScanButton({super.key});
static const Color transparent = Color(0xFFFFFFF);
static const Color transparent = Color(0x0fffffff);
@override
Widget build(BuildContext context) {
+104
View File
@@ -0,0 +1,104 @@
import 'package:flutter/material.dart';
class SkinInfo extends StatelessWidget {
final String nama;
final String asal;
final String content;
const SkinInfo({
super.key,
required this.nama,
required this.asal,
required this.content,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
children: [
SizedBox(
width: 120,
child: Text(
"Nama Pakaian",
style: const TextStyle(
letterSpacing: 0.2,
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
),
SizedBox(width: 5),
Text(
":",
style: const TextStyle(
letterSpacing: 0.2,
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
SizedBox(width: 5),
Text(
nama,
style: const TextStyle(
letterSpacing: 0.2,
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
],
),
Row(
children: [
SizedBox(
width: 120,
child: Text(
"Daerah Asal",
style: const TextStyle(
letterSpacing: 0.2,
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
),
SizedBox(width: 5),
Text(
":",
style: const TextStyle(
letterSpacing: 0.2,
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
SizedBox(width: 5),
Text(
asal,
style: const TextStyle(
letterSpacing: 0.2,
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
],
),
SizedBox(height: 20),
Text(
content,
style: const TextStyle(
letterSpacing: 0.2,
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black,
),
),
],
);
}
}
+1
View File
@@ -8,6 +8,7 @@ class TopbarContainer extends StatefulWidget {
}
class _TopbarContainerState extends State<TopbarContainer> {
@override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
return Container(
+7 -6
View File
@@ -2,19 +2,19 @@ import 'package:flutter/material.dart';
import 'package:freekake/components/navbar_container.dart';
import 'package:freekake/providers/character_provider.dart';
import 'package:freekake/providers/menu_selection_provider.dart';
import 'package:freekake/providers/point_provider.dart';
import 'package:freekake/screen/Home_screen.dart';
import 'package:provider/provider.dart';
import 'package:furibase/providers/menu_selection_provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
// SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown, // bisa dihapus jika hanya ingin satu arah
]);
// await SystemChrome.setPreferredOrientations([
// DeviceOrientation.portraitUp,
// DeviceOrientation.portraitDown, // bisa dihapus jika hanya ingin satu arah
// ]);
runApp(const MyApp());
}
@@ -28,6 +28,7 @@ class MyApp extends StatelessWidget {
providers: [
ChangeNotifierProvider(create: (context) => CharacterProvider()),
ChangeNotifierProvider(create: (context) => MenuSelectionProvider()),
ChangeNotifierProvider(create: (_) => PointsProvider()),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
+23
View File
@@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import 'package:freekake/util/player_point.dart';
class PointsProvider with ChangeNotifier {
PlayerPoints _points = PlayerPoints(xp: 0, gp: 0, sp: 0, level: 1);
PlayerPoints get points => _points;
void gainXP(int value) {
_points.addXP(value);
notifyListeners();
}
void gainGP(int value) {
_points.addGP(value);
notifyListeners();
}
void gainSP(int value) {
_points.addSP(value);
notifyListeners();
}
}
+205 -299
View File
@@ -3,20 +3,19 @@ import 'package:flutter/material.dart';
import 'package:freekake/components/bottom_navbar.dart';
import 'package:freekake/components/buildcard_info.dart';
import 'package:freekake/components/main_menu.dart';
import 'package:freekake/components/menu_item.dart';
import 'package:freekake/components/scan_button.dart';
import 'package:flutter_unity_widget/flutter_unity_widget.dart';
class HomeScreen extends StatefulWidget {
HomeScreen({super.key});
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final String username = "User01";
double _sliderValue = 0.0;
static const Color transparent = Color(0xFFFFFFFF);
final String username = "luffy01";
// Image image
late UnityWidgetController _unityWidgetController;
@@ -28,254 +27,209 @@ class _HomeScreenState extends State<HomeScreen> {
final buttonScanSize = screenWidth * 0.20;
final double bottomPadding = (9000 / screenWidth).clamp(0, 0.2);
double bottomOffset = -(screenWidth * 0.25) + 180;
double sunRiseCurve(double width) {
// kamu bisa mainin fungsi ini sesuka hati
return -((400 / width) * 100); // semakin kecil width, hasil makin negatif
}
return Scaffold(
// appBar: AppBar(
// elevation: 0,
// backgroundColor: Color(0x00000000),
// title: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Row(
// children: [
// Text(
// username,
// style: const TextStyle(
// fontSize: 18,
// fontWeight: FontWeight.bold,
// ),
// ),
// SizedBox(width: 10),
// IconButton(
// icon: Icon(Icons.emoji_events, color: Color(0xFFE32087)),
// onPressed: () {},
// ),
// Text(
// "5000",
// style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
// ),
// ],
// ),
// Row(
// children: [
// IconButton(
// icon: Icon(Icons.monetization_on, color: Color(0xFFE32087)),
// onPressed: () {},
// ),
// Text(
// "1.300",
// style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
// ),
// // IconButton(
// // icon: Image.asset("images/icon.png", width: 24),
// // onPressed: () {},
// // ),
// ],
// ),
// ],
// ),
// ),
body: Stack(
children: <Widget>[
Positioned.fill(
child: UnityWidget(
onUnityCreated: onUnityCreated,
//isARScene: true,
onUnityMessage: onUnityMessage,
//onUnitySceneLoaded: onUnitySceneLoaded,
fullscreen: false,
),
// child: Container(
// decoration: BoxDecoration(
// image: DecorationImage(
// image: AssetImage("assets/images/background.jpeg"),
// fit: BoxFit.cover,
// ),
// ),
// ),
body: homeBody(
context,
isLandscape,
bottomOffset,
bottomPadding,
screenWidth,
buttonScanSize,
),
);
}
Stack homeBody(
BuildContext context,
bool isLandscape,
double bottomOffset,
double bottomPadding,
double screenWidth,
double buttonScanSize,
) {
return Stack(
children: <Widget>[
Positioned.fill(
child: UnityWidget(
onUnityCreated: onUnityCreated,
//isARScene: true,
onUnityMessage: onUnityMessage,
//onUnitySceneLoaded: onUnitySceneLoaded,
fullscreen: false,
),
// Positioned(
// top: 0,
// left: 0,
// right: 0,
// child: SizedBox(height: 120, child: TopbarContainer()),
// ),
// Positioned(
// right: 0,
// top: 100,
// child: Padding(
// padding: EdgeInsets.only(right: 20),
// child: Container(
// width: 40,
// height: 40,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(1),
// color: Color.fromARGB(1, 209, 174, 174).withAlpha(0),
// ),
// child: Center(
// child: DropdownButtonHideUnderline(
// child: DropdownButton2(
// customButton: Container(
// height: 50,
// width: 50,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(10),
// image: const DecorationImage(
// image: AssetImage('assets/images/luffy-u.png'),
// fit: BoxFit.cover,
// ),
// ),
// ),
// openWithLongPress: false,
// items: [
// ..._MenuItems.firstItems.map(
// (item) => DropdownMenuItem<_MenuItem>(
// value: item,
// child: _MenuItems.buildItem(item),
// ),
// ),
// // const DropdownMenuItem<Divider>(
// // enabled: false,
// // // height: 8,
// // child: Divider(),
// // ),
// // ..._MenuItems.secondItems.map(
// // (item) => DropdownMenuItem<_MenuItem>(
// // value: item,
// // // height: 48,
// // child: _MenuItems.buildItem(item),
// // ),
// // ),
// ],
// onChanged: (value) {
// if (value != null) {
// _MenuItems.onChanged(context, value);
// } else {
// print("null");
// }
// },
// buttonStyleData: ButtonStyleData(
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(40),
// ),
// ),
// dropdownStyleData: DropdownStyleData(
// width: 140,
// padding: EdgeInsets.zero,
// maxHeight: 150,
// // padding: const EdgeInsets.symmetric(vertical: 6),
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(4),
// color: Color.fromRGBO(2, 104, 17, 1).withAlpha(50),
// ),
// offset: const Offset(40, -4),
// ),
// menuItemStyleData: MenuItemStyleData(
// height: 35,
// padding: EdgeInsets.symmetric(horizontal: 10),
// ),
// // menuItemStyleData: const MenuItemStyleData(
// // padding: EdgeInsets.only(left: 16, right: 16),
// // ),
// ),
// ),
// ),
// ),
// ),
// ),
Positioned(
top: 10,
left: 10,
right: 10,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
BuildcardInfo(
icon: Icons.person,
text: username,
extraIcon: Icons.emoji_events,
extraText: "5000",
width: 180,
),
BuildcardInfo(
icon: Icons.monetization_on,
text: "1.300",
width: 90,
),
],
),
),
// Positioned(
// bottom: 150,
// left: 0,
// right: 0,
// child: Column(
// children: [
// Container(
// padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
// decoration: BoxDecoration(
// color: Color.fromARGB(255, 247, 224, 236),
// borderRadius: BorderRadius.circular(10),
// boxShadow: [
// BoxShadow(
// color: Color.fromARGB(255, 214, 213, 121),
// blurRadius: 5,
// spreadRadius: 2,
// ),
// ],
// ),
// child: Text(
// "Halo....Jotaslim",
// style: TextStyle(
// fontSize: 12,
// fontWeight: FontWeight.bold,
// color: Color(0xFF000000),
// ),
// ),
// ),
// SizedBox(height: 10),
// setHomeImage("images/luffy.png", 300),
// // Image.asset("images/luffy.png", width: 300),
// ],
// ),
// ),
// BG MEnu
Stack(
),
// Positioned.fill(
// child: RepaintBoundary(
// child: Container(
// decoration: const BoxDecoration(
// image: DecorationImage(
// image: AssetImage("assets/images/background.jpeg"),
// fit: BoxFit.cover,
// ),
// ),
// ),
// ),
// ),
// Positioned(
// right: 0,
// top: 100,
// child: Padding(
// padding: EdgeInsets.only(right: 20),
// child: Container(
// width: 40,
// height: 40,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(1),
// color: Color.fromARGB(1, 209, 174, 174).withAlpha(0),
// ),
// child: Center(
// child: DropdownButtonHideUnderline(
// child: DropdownButton2(
// customButton: Container(
// height: 50,
// width: 50,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(10),
// image: const DecorationImage(
// image: AssetImage('assets/images/cepot-u.png'),
// fit: BoxFit.cover,
// ),
// ),
// ),
// openWithLongPress: false,
// items: [
// ..._MenuItems.firstItems.map(
// (item) => DropdownMenuItem<MenuItem>(
// value: item,
// child: _MenuItems.buildItem(item),
// ),
// ),
// ],
// onChanged: (value) {
// if (value != null) {
// _MenuItems.onChanged(context, value);
// } else {
// print("null");
// }
// },
// buttonStyleData: ButtonStyleData(
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(40),
// ),
// ),
// dropdownStyleData: DropdownStyleData(
// width: 140,
// padding: EdgeInsets.zero,
// maxHeight: 150,
// // padding: const EdgeInsets.symmetric(vertical: 6),
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(4),
// color: Color.fromRGBO(2, 104, 17, 1).withAlpha(50),
// ),
// offset: const Offset(40, -4),
// ),
// menuItemStyleData: MenuItemStyleData(
// height: 35,
// padding: EdgeInsets.symmetric(horizontal: 10),
// ),
// // menuItemStyleData: const MenuItemStyleData(
// // padding: EdgeInsets.only(left: 16, right: 16),
// // ),
// ),
// ),
// ),
// ),
// ),
// ),
Positioned(
top: 10,
left: 10,
right: 10,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Positioned(
bottom: 0,
left: 0,
right: 0,
child: SizedBox(height: 85, child: BottomNavbar()),
BuildcardInfo(
icon: Icons.person,
text: username,
extraIcon: Icons.emoji_events,
extraText: "5000",
width: 180,
),
BuildcardInfo(
icon: Icons.monetization_on,
text: "1.300",
width: 90,
),
],
),
Positioned(bottom: 0, left: 0, right: 0, child: MainMenu()),
Positioned(
// bottom: -150, //bottomPadding * 32,
// bottom: sunRiseCurve(
// screenWidth,
// ),
bottom:
isLandscape
? bottomOffset.clamp(-200.0, -50.0)
: bottomPadding * 10,
left: (screenWidth - buttonScanSize) / 2 - 5,
child: Transform.translate(
offset: Offset(0, -30),
child: ScanButton(),
),
// Positioned(
// bottom: 150,
// left: 0,
// right: 0,
// child: Column(
// children: [
// Container(
// padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
// decoration: BoxDecoration(
// color: Color.fromARGB(255, 247, 224, 236),
// borderRadius: BorderRadius.circular(10),
// boxShadow: [
// BoxShadow(
// color: Color.fromARGB(255, 214, 213, 121),
// blurRadius: 5,
// spreadRadius: 2,
// ),
// ],
// ),
// child: Text(
// "Halo....Jotaslim",
// style: TextStyle(
// fontSize: 12,
// fontWeight: FontWeight.bold,
// color: Color(0xFF000000),
// ),
// ),
// ),
// SizedBox(height: 10),
// //setHomeImage("assets/images/cepott.png", 300),
// ],
// ),
// ),
// BG MEnu
Stack(
children: [
Positioned(
bottom: 0,
left: 0,
right: 0,
child: SizedBox(height: 85, child: BottomNavbar()),
),
],
),
Positioned(bottom: 0, left: 0, right: 0, child: MainMenu()),
Positioned(
bottom:
isLandscape
? bottomOffset.clamp(-200.0, -50.0)
: bottomPadding * 55,
left: (screenWidth - buttonScanSize) / 2,
child: Transform.translate(
offset: Offset(0, -30),
child: ScanButton(),
),
],
),
),
],
);
}
Widget setHomeImage(String src, double size) {
return Image.asset(src, width: size);
}
void onUnityCreated(controller) {
_unityWidgetController = controller;
}
@@ -283,74 +237,26 @@ class _HomeScreenState extends State<HomeScreen> {
void onUnityMessage(message) {
print('Received message from unity: ${message.toString()}');
}
// Widget _menuButton(String label, Widget icon) {
// return Expanded(
// child: Column(
// children: [
// IconButton(
// // icon: Icon(icon, color: Color.fromARGB(255, 179, 161, 170)),
// icon: icon,
// onPressed: () {},
// ),
// Text(
// label,
// style: TextStyle(
// color: Color.fromARGB(255, 239, 224, 232),
// fontWeight: FontWeight.bold,
// ),
// ),
// ],
// ),
// );
// }
// Widget _scanButton() {
// return Expanded(
// child: Container(
// decoration: BoxDecoration(shape: BoxShape.circle, color: transparent),
// padding: EdgeInsets.all(10),
// child: IconButton(
// icon: SvgPicture.asset(
// 'icons/Scan.svg',
// width: 60,
// height: 60,
// colorFilter: ColorFilter.mode(Color(0xFFFFFFFF), BlendMode.srcIn),
// ),
// onPressed: () {},
// ),
// ),
// );
// }
Widget setHomeImage(String src, double size) {
return Image.asset("assets/" + src, width: size);
}
}
class _MenuItem {
const _MenuItem({required this.text, required this.icon});
final String text;
final IconData icon;
}
class _MenuItems {
static const List<_MenuItem> firstItems = [like, share, download];
static const List<_MenuItem> secondItems = [cancel];
static const like = _MenuItem(text: 'Like', icon: Icons.favorite);
static const share = _MenuItem(text: 'Share', icon: Icons.share);
static const download = _MenuItem(text: 'Download', icon: Icons.download);
static const cancel = _MenuItem(text: 'Cancel', icon: Icons.cancel);
static const List<MenuItem> firstItems = [like, share, download];
static const List<MenuItem> secondItems = [cancel];
static const like = MenuItem(text: 'Hello', icon: Icons.favorite);
static const share = MenuItem(text: 'Furikake', icon: Icons.share);
static const download = MenuItem(
text: 'Makanan Bergizi',
icon: Icons.download,
);
static const cancel = MenuItem(text: 'Cancel', icon: Icons.cancel);
static Widget buildItem(_MenuItem item) {
static Widget buildItem(MenuItem item) {
return Container(
height: 30,
padding: EdgeInsets.symmetric(vertical: 2),
alignment: Alignment.centerLeft,
child: Row(
children: [
// Icon(item.icon, color: Color.fromRGBO(242, 240, 240, 1), size: 22),
// const SizedBox(width: 10),
Expanded(
child: Text(
item.text,
@@ -364,7 +270,7 @@ class _MenuItems {
);
}
static void onChanged(BuildContext context, _MenuItem item) {
static void onChanged(BuildContext context, MenuItem item) {
switch (item) {
case _MenuItems.like:
//Do something
+2
View File
@@ -5,6 +5,8 @@ import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
class CameraScreen extends StatefulWidget {
const CameraScreen({super.key});
@override
_CameraScreenState createState() => _CameraScreenState();
}
+40 -13
View File
@@ -1,65 +1,92 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:freekake/components/characcter_info.dart';
class CharacterView extends StatefulWidget {
final String title;
final String imagePath;
final String content;
// caracter info
final String charName;
final String charOrigin;
final String charSex;
const CharacterView({
Key? key,
super.key,
required this.title,
required this.imagePath,
required this.content,
}) : super(key: key);
required this.charName,
required this.charSex,
required this.charOrigin,
});
@override
_CharacterViewState createState() => _CharacterViewState();
}
class _CharacterViewState extends State<CharacterView> {
String _content = '';
late String _content;
late String _charName;
late String _charOrigin;
late String _charSex;
@override
void initState() {
super.initState();
_content = widget.content;
_charName = widget.charName;
_charSex = widget.charSex;
_charOrigin = widget.charOrigin;
}
@override
Widget build(BuildContext context) {
final screenHeight = MediaQuery.of(context).size.height;
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text(widget.title),
backgroundColor: Color.fromARGB(225, 79, 76, 182),
backgroundColor: const Color.fromARGB(255, 79, 76, 182),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Gambar 1/3 dari tinggi layar
SizedBox(
height: 10,
height: 20,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(40),
bottomRight: Radius.circular(40),
),
color: Color.fromARGB(225, 79, 76, 182),
color: Color.fromARGB(255, 79, 76, 182),
),
),
),
const SizedBox(height: 0),
SizedBox(height: 10),
Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
height: screenHeight / 2,
width: double.infinity,
child: Image.asset(widget.imagePath, fit: BoxFit.fitHeight),
),
),
// Konten text scrollable
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.only(left: 18.0, right: 18, bottom: 18),
child: Text(_content),
padding: const EdgeInsets.all(18.0),
child: CharacterInfo(
nama: _charName,
kelamin: _charSex,
asal: _charOrigin,
content: _content,
),
),
),
],
),
// ),
);
}
}
@@ -32,16 +32,40 @@ class _CollectionCaraterScreenState extends State<CollectionCaraterScreen> {
// runSpacing: 10, // Vertical spacing between lines
children: [
CollectionContainer(
label: "Luffy",
imagesrc: 'assets/images/luffy.png',
colorContiner: Color.fromRGBO(12, 199, 215, 1),
onTapAc: () => {},
),
CollectionContainer(
label: "Oni Chan",
imagesrc: 'assets/images/klipartz.png',
onTapAc: () => {},
label: "Cepot",
imagesrc: 'assets/images/cepott.png',
colorContiner: Color.fromRGBO(237, 207, 100, 1.0),
onTapAc:
() => {
Navigator.push(
context,
MaterialPageRoute(
builder:
(context) => CharacterView(
title: "Tentang Cepot",
imagePath: 'assets/images/cepott.png',
charName: "Cepot",
charSex: "Laki-laki",
charOrigin: "Jawa Barat",
content:
"""Cepot adalah sosok yang begitu menarik—aneh, ceria, dan penuh kejutan! Ia muncul seperti tokoh dari dongeng lama yang lupa pulang ke rumah, dengan wajah merah seperti apel matang, hidung besar yang tampak siap mencium rahasia, dan mata bulat yang selalu bersinar penuh rasa ingin tahu. Ia suka tertawa keras, suka bermain-main dengan kata-kata, dan sangat pandai membuat siapa pun merasa nyaman di dekatnya. \nMeski kadang terlihat konyol, Cepot sebenarnya sangat cerdik—ia tahu kapan harus berbicara, kapan harus diam, dan yang paling penting, ia tahu bagaimana menghibur orang-orang yang sedang sedih. \nAnak-anak menyukainya, orang tua menghormatinya, dan tak jarang hewan-hewan kecil pun tampak betah duduk di dekatnya saat ia bercerita.
""",
),
),
),
},
),
// CollectionContainer(
// label: "Luffy",
// imagesrc: 'assets/images/luffy.png',
// colorContiner: Color.fromRGBO(12, 199, 215, 1),
// onTapAc: () => {},
// ),
// CollectionContainer(
// label: "Oni Chan",
// imagesrc: 'assets/images/klipartz.png',
// onTapAc: () => {},
// ),
],
),
),
@@ -55,22 +79,40 @@ class _CollectionCaraterScreenState extends State<CollectionCaraterScreen> {
spacing: 10, // Horizontal spacing between the containers
// runSpacing: 10, // Vertical spacing between lines
children: [
CollectionContainer(
label: "Luffy",
imagesrc: 'assets/images/luffy.png',
colorContiner: Color.fromRGBO(12, 199, 215, 1),
onTapAc: () => {},
),
CollectionContainer(
label: "Oni Chan",
imagesrc: 'assets/images/klipartz.png',
onTapAc: () => {},
),
// CollectionContainer(
// label: "Luffy",
// imagesrc: 'assets/images/luffy.png',
// colorContiner: Color.fromRGBO(12, 199, 215, 1),
// onTapAc: () => {},
// ),
// CollectionContainer(
// label: "Oni Chan",
// imagesrc: 'assets/images/klipartz.png',
// onTapAc: () => {},
// ),
CollectionContainer(
label: "Cepot",
imagesrc: 'assets/images/cepott.png',
colorContiner: Color.fromRGBO(237, 207, 100, 1.0),
onTapAc: () => {},
onTapAc:
() => {
Navigator.push(
context,
MaterialPageRoute(
builder:
(context) => CharacterView(
title: "Tentang Cepot",
imagePath: 'assets/images/cepott.png',
charName: "Cepot",
charSex: "Laki-laki",
charOrigin: "Jawa Barat",
content:
"""Cepot adalah sosok yang begitu menarik—aneh, ceria, dan penuh kejutan! Ia muncul seperti tokoh dari dongeng lama yang lupa pulang ke rumah, dengan wajah merah seperti apel matang, hidung besar yang tampak siap mencium rahasia, dan mata bulat yang selalu bersinar penuh rasa ingin tahu. Ia suka tertawa keras, suka bermain-main dengan kata-kata, dan sangat pandai membuat siapa pun merasa nyaman di dekatnya. \nMeski kadang terlihat konyol, Cepot sebenarnya sangat cerdik—ia tahu kapan harus berbicara, kapan harus diam, dan yang paling penting, ia tahu bagaimana menghibur orang-orang yang sedang sedih. \nAnak-anak menyukainya, orang tua menghormatinya, dan tak jarang hewan-hewan kecil pun tampak betah duduk di dekatnya saat ia bercerita.
""",
),
),
),
},
),
],
),
@@ -27,14 +27,14 @@ class _CollectionFragmentScreenState extends State<CollectionFragmentScreen> {
spacing: 10, // Horizontal spacing between the containers
runSpacing: 10, // Vertical spacing between lines
children: [
// CollectionContainer(
// label: "Luffy",
// imagesrc: 'assets/images/ft_luffy.png',
// onTapAc: () => {},
// colorContiner: ColorHelper.hexToColor("0xE2E5E9"),
// ),
CollectionContainer(
label: "Luffy",
imagesrc: 'assets/images/ft_luffy.png',
onTapAc: () => {},
colorContiner: ColorHelper.hexToColor("0xE2E5E9"),
),
CollectionContainer(
label: "Oni Chan",
label: "Cepot",
imagesrc: 'assets/images/ft_cepot.png',
onTapAc: () => {},
colorContiner: ColorHelper.hexToColor("0xE2E5E9"),
@@ -11,7 +11,7 @@ class CollectionSkinScreen extends StatefulWidget {
class _CollectionSkinScreenState extends State<CollectionSkinScreen> {
@override
Widget build(BuildContext context) {
return Container(
return SizedBox(
// scrollDirection: Axis.horizontal,
height: MediaQuery.of(context).size.height,
child: Column(
@@ -23,53 +23,70 @@ class _CollectionSkinScreenState extends State<CollectionSkinScreen> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Luffy", style: TextStyle(color: Colors.black)),
Divider(height: 10),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
spacing: 10, // Horizontal spacing between the containers
children: [
CollectionContainer(
label: "Luffy Kimono",
imagesrc: 'assets/images/luffy-kimono.png',
colorContiner: Color.fromRGBO(12, 199, 215, 1),
onTapAc: () => {},
),
// Text("Luffy", style: TextStyle(color: Colors.black)),
// Divider(height: 10),
// SingleChildScrollView(
// scrollDirection: Axis.horizontal,
// child: Row(
// spacing: 10, // Horizontal spacing between the containers
// children: [
// CollectionContainer(
// label: "Luffy Kimono",
// imagesrc: 'assets/images/luffy-kimono.png',
// colorContiner: Color.fromRGBO(12, 199, 215, 1),
// onTapAc: () => {},
// ),
CollectionContainer(
label: "Luffy (Default)",
imagesrc: 'assets/images/luffy.png',
colorContiner: Color.fromRGBO(12, 199, 215, 1),
onTapAc: () => {},
),
CollectionContainer(
label: "Luffy King",
imagesrc: 'assets/images/luffy-king.png',
colorContiner: Color.fromRGBO(12, 199, 215, 1),
onTapAc: () => {},
),
],
),
),
Divider(height: 20, color: Colors.transparent),
Text("Lainnya", style: TextStyle(color: Colors.black)),
// CollectionContainer(
// label: "Luffy (Default)",
// imagesrc: 'assets/images/luffy.png',
// colorContiner: Color.fromRGBO(12, 199, 215, 1),
// onTapAc: () => {},
// ),
// CollectionContainer(
// label: "Luffy King",
// imagesrc: 'assets/images/luffy-king.png',
// colorContiner: Color.fromRGBO(12, 199, 215, 1),
// onTapAc: () => {},
// ),
// ],
// ),
// ),
// Divider(height: 20, color: Colors.transparent),
Text("Cepot", style: TextStyle(color: Colors.black)),
Divider(height: 10),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
spacing: 10,
children: [
CollectionContainer(
label: "Oni Chan (Default)",
imagesrc: 'assets/images/klipartz.png',
onTapAc: () => {},
),
// CollectionContainer(
// label: "Oni Chan (Default)",
// imagesrc: 'assets/images/klipartz.png',
// onTapAc: () => {},
// ),
CollectionContainer(
label: "Cepot (Default)",
imagesrc: 'assets/images/cepott.png',
colorContiner: Color.fromRGBO(237, 207, 100, 1.0),
onTapAc: () => {},
onTapAc:
() => {
Navigator.push(
context,
MaterialPageRoute(
builder:
(context) => SkinView(
title: "Tentang Skin",
imagePath: 'assets/images/cepott.png',
skinName: "Pangsi",
skinOrigin: "Jawa Barat",
content:
"""Pakaian Cepot juga punya makna, lho! Ia selalu memakai baju pangsi hitam panjang yang sederhana, melambangkan kesederhanaan dan kerendahan hati. \nBaju ini longgar dan nyaman, cocok untuk Cepot yang suka bergerak bebas dan menghibur orang-orang di sekitarnya. \nSarung kotak-kotak yang dililit di pinggang bukan hanya pelengkap, tapi juga simbol budaya rakyat biasa yang tetap rapi dan terhormat. \nDan jangan lupa iket batik di kepalanya—itu bukan cuma hiasan, tapi lambang kebanggaan dan identitas sebagai orang Sunda. Lewat pakaiannya, Cepot mengajarkan kita bahwa kita tidak perlu pakaian mahal untuk terlihat hebat. Yang penting adalah sikap baik, rasa hormat pada budaya, dan keberanian untuk jadi diri sendiri!
""",
),
),
),
},
),
],
),
+87
View File
@@ -0,0 +1,87 @@
import 'package:flutter/material.dart';
import 'package:freekake/components/characcter_info.dart';
import 'package:freekake/components/skin_info.dart';
class SkinView extends StatefulWidget {
final String title;
final String imagePath;
final String content;
// caracter info
final String skinName;
final String skinOrigin;
const SkinView({
super.key,
required this.title,
required this.imagePath,
required this.content,
required this.skinName,
required this.skinOrigin,
});
@override
_SkinViewState createState() => _SkinViewState();
}
class _SkinViewState extends State<SkinView> {
late String _content;
late String _skinName;
late String _skinOrigin;
@override
void initState() {
super.initState();
_content = widget.content;
_skinName = widget.skinName;
_skinOrigin = widget.skinOrigin;
}
@override
Widget build(BuildContext context) {
final screenHeight = MediaQuery.of(context).size.height;
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text(widget.title),
backgroundColor: const Color.fromARGB(255, 79, 76, 182),
),
body: Column(
children: [
SizedBox(
height: 20,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(40),
bottomRight: Radius.circular(40),
),
color: Color.fromARGB(255, 79, 76, 182),
),
),
),
SizedBox(height: 10),
Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
height: screenHeight / 2,
width: double.infinity,
child: Image.asset(widget.imagePath, fit: BoxFit.fitHeight),
),
),
// Konten text scrollable
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(18.0),
child: SkinInfo(
nama: _skinName,
asal: _skinOrigin,
content: _content,
),
),
),
],
),
);
}
}
+117 -109
View File
@@ -41,127 +41,135 @@ class _KoleksiScreenState extends State<KoleksiScreen> {
final double bottomPadding = (9000 / screenWidth).clamp(0, 0.2);
return Scaffold(
extendBody: true,
body: Stack(
children: <Widget>[
Positioned.fill(
child: Container(color: const Color.fromARGB(255, 255, 255, 255)),
),
Positioned.fill(
top: 30,
child: PageView(
controller: _pageController,
onPageChanged: (index) {
setState(() {
_selectedIndex = index;
});
},
children: [
const CollectionCaraterScreen(),
const CollectionSkinScreen(),
const CollectionFragmentScreen(),
],
),
),
// BG MEnu
Stack(
body: koleksiBody(bottomPadding, screenWidth, buttonScanSize),
);
}
Stack koleksiBody(
double bottomPadding,
double screenWidth,
double buttonScanSize,
) {
return Stack(
children: <Widget>[
Positioned.fill(
child: Container(color: const Color.fromARGB(255, 255, 255, 255)),
),
Positioned.fill(
top: 30,
child: PageView(
controller: _pageController,
onPageChanged: (index) {
setState(() {
_selectedIndex = index;
});
},
children: [
Positioned(
bottom: 0,
left: 0,
right: 0,
child: SizedBox(height: 85, child: BottomNavbar()),
const CollectionCaraterScreen(),
const CollectionSkinScreen(),
const CollectionFragmentScreen(),
],
),
),
// BG MEnu
Stack(
children: [
Positioned(
bottom: 0,
left: 0,
right: 0,
child: SizedBox(height: 85, child: BottomNavbar()),
),
],
),
//TopBar
Positioned(
top: 0,
left: 0,
right: 0,
child: SizedBox(height: 130, child: TopbarContainer()),
),
Positioned(
top: 10,
left: 10,
right: 10,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
BuildcardInfo(
icon: Icons.person,
text: 'User01',
extraIcon: Icons.emoji_events,
extraText: "5000",
width: 180,
),
BuildcardInfo(
icon: Icons.monetization_on,
text: "1.300",
width: 90,
),
],
),
//TopBar
Positioned(
top: 0,
left: 0,
right: 0,
child: SizedBox(height: 130, child: TopbarContainer()),
),
Positioned(
top: 10,
left: 10,
right: 10,
),
// Positioned.fill(
// top: 30,
// child: PageView(
// controller: _pageController,
// onPageChanged: (index) {
// setState(() {
// _selectedIndex = index;
// });
// },
// children: [
// const CollectionCaraterScreen(),
// const CollectionSkinScreen(),
// const CollectionFragmentScreen(),
// ],
// ),
// ),
Positioned(
top: 70,
left: 10,
right: 10,
child: Container(
// decoration: const BoxDecoration(
// borderRadius: BorderRadius.all(Radius.circular(20)),
// color: Color.fromARGB(116, 125, 129, 133),
// ),
padding: const EdgeInsets.all(1),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
BuildcardInfo(
icon: Icons.person,
text: 'User01',
extraIcon: Icons.emoji_events,
extraText: "5000",
width: 180,
CurvedBottomBorderTab(
label: "Karakter",
isSelected: _selectedIndex == 0,
tap: () => _onMenuTapped(0),
),
BuildcardInfo(
icon: Icons.monetization_on,
text: "1.300",
width: 90,
CurvedBottomBorderTab(
label: "Skin",
isSelected: _selectedIndex == 1,
tap: () => _onMenuTapped(1),
),
CurvedBottomBorderTab(
label: "Fragment",
isSelected: _selectedIndex == 2,
tap: () => _onMenuTapped(2),
),
],
),
),
// Positioned.fill(
// top: 30,
// child: PageView(
// controller: _pageController,
// onPageChanged: (index) {
// setState(() {
// _selectedIndex = index;
// });
// },
// children: [
// const CollectionCaraterScreen(),
// const CollectionSkinScreen(),
// const CollectionFragmentScreen(),
// ],
// ),
// ),
Positioned(
top: 70,
left: 10,
right: 10,
child: Container(
// decoration: const BoxDecoration(
// borderRadius: BorderRadius.all(Radius.circular(20)),
// color: Color.fromARGB(116, 125, 129, 133),
// ),
padding: const EdgeInsets.all(1),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
CurvedBottomBorderTab(
label: "Karakter",
isSelected: _selectedIndex == 0,
tap: () => _onMenuTapped(0),
),
CurvedBottomBorderTab(
label: "Skin",
isSelected: _selectedIndex == 1,
tap: () => _onMenuTapped(1),
),
CurvedBottomBorderTab(
label: "Fragment",
isSelected: _selectedIndex == 2,
tap: () => _onMenuTapped(2),
),
],
),
),
),
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(),
),
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(),
),
),
],
),
),
],
);
}
}
+209 -217
View File
@@ -2,14 +2,13 @@ import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:freekake/components/buildcard_info.dart';
import 'package:freekake/components/card_list.dart';
import 'package:freekake/components/collection_container%20copy.dart';
import 'package:freekake/components/main_menu.dart';
import 'package:freekake/components/topbar_container.dart';
import 'package:freekake/helpers/color_helper.dart';
import 'package:freekake/screen/pustaka/pustaka_detail_screen.dart';
import 'package:freekake/helpers/color_helper.dart';
class ListEducation extends StatefulWidget {
const ListEducation({super.key});
@@ -43,9 +42,12 @@ class _ListEducationState extends State<ListEducation> {
final List<Map<String, dynamic>> _listContent = [
{
"title": "Taburan Lezat untuk Nasi!",
"image": "assets/icons/healthy.svg",
"image": "assets/html/furikake/furikake.jpg",
"color": "#cdd0ee",
"category": "Kesehatan",
"type": "Materi",
"point": "10",
"coint": "100",
"body":
"""Tahukah kamu bahwa di Jepang ada bumbu tabur yang bisa membuat nasi
menjadi lebih enak?
@@ -54,9 +56,12 @@ class _ListEducationState extends State<ListEducation> {
},
{
"title": "Makan Sehat dengan Gizi Seimbang",
"image": "assets/icons/Safety.svg",
"image": "assets/html/lemak.png",
"color": "#cef1da",
"category": "Keselamatan",
"type": "Materi",
"point": "100",
"coint": "20",
"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)
@@ -82,7 +87,7 @@ class _ListEducationState extends State<ListEducation> {
}
void _loadContent(String path) async {
if (_shouldShowWebView && _controller != null) {
if (_shouldShowWebView) {
if (path.startsWith('http')) {
// Load dari Internet
_controller.loadRequest(Uri.parse(path));
@@ -121,8 +126,8 @@ class _ListEducationState extends State<ListEducation> {
@override
Widget build(BuildContext context) {
String _searchQuery = "";
final List<Map<String, dynamic>> _collections = [
String searchQuery = "";
final List<Map<String, dynamic>> collections = [
{
"label": "Kesehatan",
"image": "assets/icons/healthy.svg",
@@ -150,14 +155,14 @@ class _ListEducationState extends State<ListEducation> {
];
final List<Map<String, dynamic>> filteredCollections =
_collections
collections
.where(
(item) => item["label"].toLowerCase().contains(
_searchQuery.toLowerCase(),
searchQuery.toLowerCase(),
),
)
.toList();
if (_shouldShowWebView && _controller != null) {
if (_shouldShowWebView) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color.fromARGB(225, 79, 76, 182),
@@ -171,215 +176,10 @@ class _ListEducationState extends State<ListEducation> {
body: Column(
children: [
// Header Section
Container(
width: MediaQuery.of(context).size.width,
height: 150,
decoration: BoxDecoration(
color: const Color.fromARGB(225, 79, 76, 182),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(25),
bottomRight: Radius.circular(25),
),
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 6,
),
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('');
},
),
],
),
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children:
filteredCollections.map((item) {
return CollectionContainer(
label: item["label"]!,
imageSvg: item["image"]!,
iconw: 20,
iconh: 20,
width: 70,
height: 70,
textColor: Colors.black,
lblSize: 10,
colorContiner: ColorHelper.hexToColor(
item["color"],
),
onTapAc:
() => {
searchController.clear(),
filterByCategory(item["category"]),
},
);
}).toList(),
),
],
),
),
headerContainer(context, filteredCollections),
// Collection Items dengan Filter
SizedBox(height: 20),
Expanded(
child: Container(
decoration: BoxDecoration(
color: const Color.fromARGB(223, 200, 200, 206),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(25),
topRight: Radius.circular(25),
),
),
child: Column(
children: [
SizedBox(height: 5),
Padding(
padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width / 4,
),
child: Divider(
color: const Color.fromARGB(255, 255, 255, 255),
thickness: 2,
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
// vertical: 10,
),
child:
filteredItems.isEmpty
? const Center(
child: Text(
"Tidak ada hasil",
style: TextStyle(
fontSize: 16,
color: Colors.black54,
),
),
)
: Padding(
padding: EdgeInsets.only(
top: 10.0,
left: 10,
right: 10,
),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
color: const Color.fromARGB(
255,
222,
181,
133,
),
),
child: Padding(
padding: EdgeInsets.only(
top: 12,
right: 20,
left: 20,
),
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1, // 2 Kolom
crossAxisSpacing: 10,
mainAxisSpacing: 10,
childAspectRatio: 2.5,
),
itemCount: _listContent.length,
itemBuilder: (context, index) {
return GestureDetector(
child: CardList(
title:
_listContent[index]["title"] ??
"",
body:
_listContent[index]["body"] ??
"",
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder:
(
context,
) => listDetailExtension.buildPage({
'title':
filteredItems[index]['category'],
'imagePath':
filteredItems[index]['image'],
'filepath':
filteredItems[index]['file'],
'paragraphs':
"""
""" +
"<h1> ${_listContent[index]["title"]} </h1> <p> ${_listContent[index]["body"]}",
}),
),
);
},
);
},
),
),
),
),
),
),
],
),
),
),
Expanded(child: contentContainer(context)),
],
),
);
@@ -392,4 +192,196 @@ class _ListEducationState extends State<ListEducation> {
);
}
}
Container headerContainer(
BuildContext context,
List<Map<String, dynamic>> filteredCollections,
) {
return Container(
width: MediaQuery.of(context).size.width,
height: 150,
decoration: BoxDecoration(
color: const Color.fromARGB(225, 79, 76, 182),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(25),
bottomRight: Radius.circular(25),
),
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 6),
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('');
},
),
],
),
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children:
filteredCollections.map((item) {
return CollectionContainer(
label: item["label"]!,
imageSvg: item["image"]!,
iconw: 20,
iconh: 20,
width: 70,
height: 70,
textColor: Colors.black,
lblSize: 8,
colorContiner: ColorHelper.hexToColor(item["color"]),
onTapAc:
() => {
searchController.clear(),
filterByCategory(item["category"]),
},
);
}).toList(),
),
],
),
);
}
Container contentContainer(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: const Color.fromARGB(223, 200, 200, 206),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(25),
topRight: Radius.circular(25),
),
),
child: _listCard(context),
);
}
Column _listCard(BuildContext context) {
return Column(
children: [
SizedBox(height: 5),
Padding(
padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width / 4,
),
child: Divider(
color: const Color.fromARGB(255, 255, 255, 255),
thickness: 2,
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
// vertical: 10,
),
child:
filteredItems.isEmpty
? const Center(
child: Text(
"Tidak ada hasil",
style: TextStyle(fontSize: 16, color: Colors.black54),
),
)
: Padding(
padding: EdgeInsets.only(top: 10.0, left: 10, right: 10),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
color: const Color.fromARGB(255, 222, 181, 133),
),
child: Padding(
padding: EdgeInsets.only(
top: 12,
right: 20,
left: 20,
),
child: _generateList(),
),
),
),
),
),
],
);
}
GridView _generateList() {
return GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1, // 2 Kolom
crossAxisSpacing: 10,
mainAxisSpacing: 10,
childAspectRatio: 2.5,
),
itemCount: _listContent.length,
itemBuilder: (context, index) {
return GestureDetector(
child: CardList(
title: _listContent[index]["title"] ?? "",
body: _listContent[index]["body"] ?? "",
gambar: _listContent[index]['image'],
point: _listContent[index]['coint'],
coint: _listContent[index]['point'],
tipe: _listContent[index]['tipe'],
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder:
(context) => listDetailExtension.buildPage({
'title': filteredItems[index]['category'],
'imagePath': filteredItems[index]['image'],
'filepath': filteredItems[index]['file'],
'paragraphs':
"""
"""
"<h1> ${_listContent[index]["title"]} </h1> <p> ${_listContent[index]["body"]}",
}),
),
);
},
);
},
);
}
}
@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:freekakes/components/buildcard_info.dart';
import 'package:freekake/components/buildcard_info.dart';
import 'package:freekake/components/collection_container.dart';
class DetailScreen extends StatefulWidget {
+144 -114
View File
@@ -2,25 +2,25 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_html/flutter_html.dart';
class ListDetailScreen extends StatefulWidget {
class ListPustakaDetailScreen extends StatefulWidget {
final String title;
final String imagePath;
final String paragraphs;
final String filepath;
const ListDetailScreen({
Key? key,
const ListPustakaDetailScreen({
super.key,
required this.title,
required this.imagePath,
required this.paragraphs,
required this.filepath,
}) : super(key: key);
});
@override
_ListDetailScreenState createState() => _ListDetailScreenState();
}
class _ListDetailScreenState extends State<ListDetailScreen> {
class _ListDetailScreenState extends State<ListPustakaDetailScreen> {
late List<bool> _readStatus;
String _htmlContent = '';
@@ -41,6 +41,11 @@ class _ListDetailScreenState extends State<ListDetailScreen> {
String html = await DefaultAssetBundle.of(
context,
).loadString(widget.filepath);
html = html.replaceAllMapped(
RegExp(r'<div class="gizi-grid">(.*?)</div>', dotAll: true),
(match) => '<gizi-grid>${match.group(1)}</gizi-grid>',
);
setState(() {
_htmlContent = html;
});
@@ -102,115 +107,73 @@ class _ListDetailScreenState extends State<ListDetailScreen> {
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();
},
),
],
),
// 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);
// // },
// // ),
// ],
// ),
// );
// },
// ),
htmlBody(),
),
),
],
@@ -218,4 +181,71 @@ class _ListDetailScreenState extends State<ListDetailScreen> {
// ),
);
}
Html htmlBody() {
return 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();
},
),
TagExtension(
tagsToExtend: {"gizi-grid"},
builder: (context) {
final spans = context.inlineSpanChildren ?? [];
final widgets =
spans.map((span) {
if (span is WidgetSpan) return span.child;
return const SizedBox();
}).toList();
return Wrap(
spacing: 12,
runSpacing: 12,
children:
widgets.map((child) {
return SizedBox(width: 160, child: child);
}).toList(),
);
},
),
],
);
}
}
+6 -6
View File
@@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:furibase/components/buildcard_info.dart';
import 'package:furibase/components/collection_container.dart';
import 'package:furibase/components/menu_button.dart';
import 'package:furibase/components/scan_button.dart';
import 'package:furibase/screen/Home_screen.dart';
import 'package:furibase/screen/pustaka/list_education.dart';
import 'package:freekake/components/buildcard_info.dart';
import 'package:freekake/components/collection_container.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/pustaka/list_education.dart';
class PustakaScreen extends StatefulWidget {
const PustakaScreen({super.key});
+6 -6
View File
@@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:furibase/components/buildcard_info.dart';
import 'package:furibase/components/collection_container.dart';
import 'package:furibase/components/menu_button.dart';
import 'package:furibase/components/scan_button.dart';
import 'package:furibase/screen/Home_screen.dart';
import 'package:furibase/screen/pustaka/list_education.dart';
import 'package:freekake/components/buildcard_info.dart';
import 'package:freekake/components/collection_container.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/pustaka/list_education.dart';
class PustakaScreen extends StatefulWidget {
const PustakaScreen({super.key});
+6 -6
View File
@@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:furibase/components/buildcard_info.dart';
import 'package:furibase/components/collection_container.dart';
import 'package:furibase/components/menu_button.dart';
import 'package:furibase/components/scan_button.dart';
import 'package:furibase/screen/Home_screen.dart';
import 'package:furibase/screen/pustaka/list_education.dart';
import 'package:freekake/components/buildcard_info.dart';
import 'package:freekake/components/collection_container.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/pustaka/list_education.dart';
class PustakaScreen extends StatefulWidget {
const PustakaScreen({super.key});
+234 -221
View File
@@ -7,14 +7,6 @@ import 'package:freekake/components/scan_button.dart';
import 'package:freekake/components/topbar_container.dart';
import 'package:freekake/helpers/color_helper.dart';
import 'package:freekake/screen/pustaka/list_education.dart';
import 'package:freekake/components/bottom_navbar.dart';
import 'package:freekake/components/buildcard_info.dart';
import 'package:freekake/components/collection_container.dart';
import 'package:freekake/components/main_menu.dart';
import 'package:freekake/components/scan_button.dart';
import 'package:freekake/components/topbar_container.dart';
import 'package:freekake/helpers/color_helper.dart';
import 'package:freekake/screen/pustaka/list_education.dart';
class PustakaScreen extends StatefulWidget {
const PustakaScreen({super.key});
@@ -30,10 +22,26 @@ class _PustakaScreenState extends State<PustakaScreen> {
String _searchQuery = "";
final List<Map<String, dynamic>> _collections = [
{"label": "Kesehatan", "image": "assets/icons/healthy.svg", "color": "#cdd0ee"},
{"label": "Gizi", "image": "assets/icons/Nutrition.svg", "color": "#e8e29a"},
{"label": "Pendidikan", "image": "assets/icons/Education.svg", "color": "#efd8c6"},
{"label": "Keselamatan", "image": "assets/icons/Safety.svg", "color": "#cef1da"},
{
"label": "Kesehatan",
"image": "assets/icons/healthy.svg",
"color": "#cdd0ee",
},
{
"label": "Gizi",
"image": "assets/icons/Nutrition.svg",
"color": "#e8e29a",
},
{
"label": "Pendidikan",
"image": "assets/icons/Education.svg",
"color": "#efd8c6",
},
{
"label": "Keselamatan",
"image": "assets/icons/Safety.svg",
"color": "#cef1da",
},
];
void _onMenuTapped(int index) {
@@ -63,204 +71,209 @@ class _PustakaScreenState extends State<PustakaScreen> {
return Scaffold(
backgroundColor: const Color.fromARGB(255, 255, 255, 255),
body: Stack(
children: [
//Top Bar
Positioned(
top: 0,
left: 0,
right: 0,
child: SizedBox(height: 130, child: TopbarContainer()),
),
Column(
children: [
// Header Section
Container(
padding: const EdgeInsets.all(10),
color: const Color.fromARGB(
250,
255,
255,
255,
).withOpacity(0.0),
body: pustakaBody(
filteredCollections,
context,
bottomPadding,
screenWidth,
buttonScanSize,
),
);
}
Stack pustakaBody(
List<Map<String, dynamic>> filteredCollections,
BuildContext context,
double bottomPadding,
double screenWidth,
double buttonScanSize,
) {
return Stack(
children: [
//Top Bar
Positioned(
top: 0,
left: 0,
right: 0,
child: SizedBox(height: 130, child: TopbarContainer()),
),
Column(
children: [
// Header Section
Container(
padding: const EdgeInsets.all(10),
color: const Color.fromARGB(250, 255, 255, 255).withOpacity(0.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
BuildcardInfo(
icon: Icons.person,
text: 'User01',
extraIcon: Icons.emoji_events,
extraText: "5000",
width: 180,
),
BuildcardInfo(
icon: Icons.monetization_on,
text: "1.300",
width: 90,
),
],
),
),
// // Date Display
// Padding(
// padding: const EdgeInsets.symmetric(vertical: 10),
// child: Text(
// DateTime.now().toString(),
// style: const TextStyle(color: Colors.white),
// ),
// ),
// Search Bar
Padding(
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 10),
child: Container(
height: 47,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: const Color.fromARGB(255, 237, 227, 227),
),
padding: const EdgeInsets.symmetric(horizontal: 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
BuildcardInfo(
icon: Icons.person,
text: 'User01',
extraIcon: Icons.emoji_events,
extraText: "5000",
width: 180,
),
BuildcardInfo(
icon: Icons.monetization_on,
text: "1.300",
width: 90,
const Icon(Icons.search, color: Colors.black54, size: 18),
const SizedBox(width: 8),
Expanded(
child: TextField(
controller: _searchController,
onChanged: (value) {
setState(() {
_searchQuery = value;
});
},
style: const TextStyle(
color: Colors.black,
fontSize: 12,
),
decoration: const InputDecoration(
border: InputBorder.none,
hintText: 'Search...',
hintStyle: TextStyle(color: Colors.grey),
),
),
),
],
),
),
// // Date Display
// Padding(
// padding: const EdgeInsets.symmetric(vertical: 10),
// child: Text(
// DateTime.now().toString(),
// style: const TextStyle(color: Colors.white),
// ),
// ),
// Search Bar
Padding(
),
// Collection Items
SizedBox(
height: 440,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 40,
vertical: 10,
horizontal: 58,
vertical: 60,
),
child: Container(
height: 47,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: const Color.fromARGB(255, 237, 227, 227),
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 140,
mainAxisSpacing: 20,
crossAxisSpacing: 20,
childAspectRatio: 1, // 1:1 rasio (kotak)
),
padding: const EdgeInsets.symmetric(horizontal: 15),
itemCount: filteredCollections.length,
itemBuilder: (context, index) {
final item = filteredCollections[index];
return CollectionContainer(
label: item["label"]!,
imageSvg: item["image"]!,
width: 30,
height: 80,
textColor: Colors.black,
circle: true,
colorContiner: ColorHelper.hexToColor(item["color"]),
onTapAc: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const ListEducation(),
),
);
},
);
},
),
),
),
// SizedBox(
// child: Padding(
// padding: const EdgeInsets.symmetric(
// horizontal: 5,
// vertical: 1,
// ),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text("List Topic", style: TextStyle(color: Colors.black)),
// Divider(color: Colors.transparent),
// ],
// ),
// ),
// ),
// Horizontal Scroll List
Padding(
padding: EdgeInsets.symmetric(horizontal: 5, vertical: 5),
child: SizedBox(
width: MediaQuery.of(context).size.width,
height: 100,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
const Icon(Icons.search, color: Colors.black54, size: 18),
const SizedBox(width: 8),
Expanded(
child: TextField(
controller: _searchController,
onChanged: (value) {
setState(() {
_searchQuery = value;
});
},
style: const TextStyle(
color: Colors.black,
fontSize: 12,
children: List.generate(
10,
(index) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
decoration: const InputDecoration(
border: InputBorder.none,
hintText: 'Cari konten...',
hintStyle: TextStyle(color: Colors.grey),
),
),
),
],
),
),
),
// Collection Items
Container(
height: 440,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 58,
vertical: 60,
),
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 140,
mainAxisSpacing: 20,
crossAxisSpacing: 20,
childAspectRatio: 1, // 1:1 rasio (kotak)
),
itemCount: filteredCollections.length,
itemBuilder: (context, index) {
final item = filteredCollections[index];
return CollectionContainer(
label: item["label"]!,
lblSize: 11,
imageSvg: item["image"]!,
width: 30,
height: 80,
textColor: Colors.black,
circle: true,
colorContiner: ColorHelper.hexToColor(item["color"]),
onTapAc: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const ListEducation(),
),
);
},
);
},
),
),
),
SizedBox(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 40,
vertical: 1,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Daftar Topik", style: TextStyle(color: Colors.black)),
Divider(color: Colors.transparent),
],
),
),
),
// Horizontal Scroll List
Padding(
padding: EdgeInsets.symmetric(horizontal: 5, vertical: 5),
child: SizedBox(
width: MediaQuery.of(context).size.width,
height: 100,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: List.generate(
10,
(index) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
elevation: 5,
color: ColorHelper.hexToColor("#efd8c6"),
// color: const Color.fromARGB(140, 255, 255, 255),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(15),
// ),
child: GestureDetector(
onTap:
() => {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ListEducation(),
),
elevation: 5,
color: ColorHelper.hexToColor("#efd8c6"),
// color: const Color.fromARGB(140, 255, 255, 255),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(15),
// ),
child: GestureDetector(
onTap:
() => {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ListEducation(),
),
},
child: Container(
width: 80,
padding: const EdgeInsets.all(10),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.book,
size: 50,
color: Colors.blueAccent,
),
},
child: Container(
width: 80,
padding: const EdgeInsets.all(10),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.book,
size: 50,
color: Colors.blueAccent,
),
Text(
'Topic ${index + 1}',
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Color.fromARGB(255, 0, 0, 0),
),
Text(
'Topik ' + (index + 1).toString(),
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 11,
color: Color.fromARGB(255, 0, 0, 0),
),
),
],
),
),
],
),
),
),
@@ -270,32 +283,32 @@ class _PustakaScreenState extends State<PustakaScreen> {
),
),
),
// Bottom Navigation
// MainMenu(),
],
),
// BG MEnu
Stack(
children: [
Positioned(
bottom: 0,
left: 0,
right: 0,
child: SizedBox(height: 85, child: BottomNavbar()),
),
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(),
),
// Bottom Navigation
// MainMenu(),
],
),
// BG MEnu
Stack(
children: [
Positioned(
bottom: 0,
left: 0,
right: 0,
child: SizedBox(height: 85, child: BottomNavbar()),
),
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(),
),
],
),
),
],
);
}
}
+4 -4
View File
@@ -1,10 +1,10 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:furibase/components/bottom_navbar.dart';
import 'package:furibase/components/main_menu.dart';
import 'package:furibase/components/scan_button.dart';
import 'package:furibase/helpers/pick_image.dart';
import 'package:freekake/components/bottom_navbar.dart';
import 'package:freekake/components/main_menu.dart';
import 'package:freekake/components/scan_button.dart';
import 'package:freekake/helpers/pick_image.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
class AkunSaya extends StatefulWidget {
+12 -20
View File
@@ -10,6 +10,8 @@ import 'dart:io';
import 'package:flutter/foundation.dart' show kIsWeb;
class AkunSaya extends StatefulWidget {
const AkunSaya({super.key});
@override
_AkunSayaState createState() => _AkunSayaState();
}
@@ -72,7 +74,7 @@ class _AkunSayaState extends State<AkunSaya> {
height: MediaQuery.of(context).size.height * 0.7,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: const Color.fromARGB(255, 205, 202, 189),
color: const Color.fromARGB(255, 212, 211, 208),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
@@ -122,16 +124,15 @@ class _AkunSayaState extends State<AkunSaya> {
style: TextStyle(color: Colors.black),
),
TextFormField(
initialValue: "Jl. Sentosa jaya",
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Alamat',
labelStyle: TextStyle(color: Colors.black),
),
style: TextStyle(color: Colors.black),
),
// TextFormField(
// initialValue: "Jl. Sentosa jaya",
// decoration: const InputDecoration(
// border: UnderlineInputBorder(),
// labelText: 'Alamat',
// labelStyle: TextStyle(color: Colors.black),
// ),
// style: TextStyle(color: Colors.black),
// ),
TextFormField(
initialValue: "******",
decoration: const InputDecoration(
@@ -141,15 +142,6 @@ class _AkunSayaState extends State<AkunSaya> {
),
style: TextStyle(color: Colors.black),
),
TextFormField(
initialValue: "+62923",
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'No. HP',
labelStyle: TextStyle(color: Colors.black),
),
style: TextStyle(color: Colors.black),
),
],
),
),
+260
View File
@@ -0,0 +1,260 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:freekake/components/bottom_navbar.dart';
import 'package:freekake/components/main_menu.dart';
import 'package:freekake/components/scan_button.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
// import 'package:image_picker_web/image_picker_web.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
class DataSaya extends StatefulWidget {
const DataSaya({super.key});
@override
_DataSayaState createState() => _DataSayaState();
}
class _DataSayaState extends State<DataSaya> {
dynamic _profileImage;
dynamic _headerImage;
ImageProvider? imageProvider;
final ImagePicker _picker = ImagePicker();
Future<void> _pickImage(bool isProfile) async {
if (kIsWeb) {
// Uint8List? bytesFromPicker = await ImagePickerWeb.getImageAsBytes();
// if (bytesFromPicker != null) {
// setState(() {
// if (isProfile) {
// _profileImage = bytesFromPicker;
// } else {
// _headerImage = bytesFromPicker;
// }
// });
// }
} else {
final pickedFile = await _picker.pickImage(source: ImageSource.gallery);
if (pickedFile != null) {
final imageFile = File(pickedFile.path);
setState(() {
if (isProfile) {
_profileImage = imageFile;
} else {
_headerImage = imageFile;
}
imageProvider =
kIsWeb ? MemoryImage(_profileImage) : FileImage(_profileImage);
});
} else {
imageProvider = AssetImage("assets/images/luffy.png");
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Data Pribadi"),
backgroundColor: Color.fromARGB(225, 79, 76, 182),
),
resizeToAvoidBottomInset: false,
backgroundColor: const Color.fromARGB(255, 255, 255, 255),
body: Stack(
children: [
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.only(top: 0.0, left: 20, right: 20),
child: Container(
height: MediaQuery.of(context).size.height * 0.7,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: const Color.fromARGB(255, 212, 211, 208),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
),
),
),
),
Column(
children: [
SizedBox(
height: 80,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(40),
bottomRight: Radius.circular(40),
),
color: Color.fromARGB(225, 79, 76, 182),
),
),
),
SizedBox(height: 70),
Expanded(
child: Padding(
padding: EdgeInsets.all(20),
child: ListView(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 40),
children: [
TextFormField(
initialValue: "Hasan",
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Nama Siswa',
labelStyle: TextStyle(color: Colors.black),
),
style: TextStyle(color: Colors.black),
),
TextFormField(
initialValue: "Jawa Barat",
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Provinsi',
labelStyle: TextStyle(color: Colors.black),
),
style: TextStyle(color: Colors.black),
),
TextFormField(
initialValue: "Bandung",
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Kabupaten',
labelStyle: TextStyle(color: Colors.black),
),
style: TextStyle(color: Colors.black),
),
TextFormField(
initialValue: "SDN 1 Bandung Barat",
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Sekolah',
labelStyle: TextStyle(color: Colors.black),
),
style: TextStyle(color: Colors.black),
),
TextFormField(
initialValue: "Laki-laki",
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Jenis Kelamin',
labelStyle: TextStyle(color: Colors.black),
),
style: TextStyle(color: Colors.black),
),
TextFormField(
initialValue: "Bandung 13-05-2025",
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Sekolah',
labelStyle: TextStyle(color: Colors.black),
),
style: TextStyle(color: Colors.black),
),
TextFormField(
initialValue: "56 Kg",
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Berat Badan',
labelStyle: TextStyle(color: Colors.black),
),
style: TextStyle(color: Colors.black),
),
TextFormField(
initialValue: "170",
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Tinggi Badan',
labelStyle: TextStyle(color: Colors.black),
),
style: TextStyle(color: Colors.black),
),
],
),
),
),
],
),
// Profile Picture (bisa diubah)
Positioned(
top: 25,
left: MediaQuery.of(context).size.width / 2 - 50,
child: GestureDetector(
onTap: () => _pickImage(true),
child: Stack(
alignment: Alignment.bottomRight,
children: [
CircleAvatar(
radius: 50,
backgroundColor: Colors.grey[300],
backgroundImage:
_profileImage != null
? (kIsWeb
? MemoryImage(_profileImage) as ImageProvider
: FileImage(_profileImage) as ImageProvider)
: null,
child:
_profileImage == null
? Image.asset("assets/images/luffy.png")
: null,
),
CircleAvatar(
radius: 15,
backgroundColor: Colors.blue,
child: Icon(Icons.edit, color: Colors.white, size: 15),
),
],
),
),
),
Positioned(
top: 130,
left: MediaQuery.of(context).size.width / 2 - 30,
child: GestureDetector(
onTap: () => _pickImage(true),
child: Stack(
alignment: Alignment.bottomRight,
children: [
Text(
"Luffy kun",
style: TextStyle(
color: Colors.deepPurple,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
],
),
);
}
Widget _buildListItem(IconData icon, String label, {bool isLogout = false}) {
return ListTile(
leading: Icon(
icon,
color:
isLogout
? const Color.fromARGB(255, 181, 47, 47)
: const Color.fromARGB(255, 255, 255, 255),
),
title: Text(label, style: TextStyle(fontSize: 16, color: Colors.black)),
trailing: Icon(Icons.arrow_forward_ios, size: 16),
onTap: () {},
);
}
}
+305
View File
@@ -0,0 +1,305 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:freekake/components/bottom_navbar.dart';
import 'package:freekake/components/main_menu.dart';
import 'package:freekake/components/point_header.dart';
import 'package:freekake/components/scan_button.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
// import 'package:image_picker_web/image_picker_web.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
class PointSaya extends StatefulWidget {
const PointSaya({super.key});
@override
_PointSayaState createState() => _PointSayaState();
}
class _PointSayaState extends State<PointSaya> {
dynamic _profileImage;
dynamic _headerImage;
ImageProvider? imageProvider;
final ImagePicker _picker = ImagePicker();
Future<void> _pickImage(bool isProfile) async {
if (kIsWeb) {
// Uint8List? bytesFromPicker = await ImagePickerWeb.getImageAsBytes();
// if (bytesFromPicker != null) {
// setState(() {
// if (isProfile) {
// _profileImage = bytesFromPicker;
// } else {
// _headerImage = bytesFromPicker;
// }
// });
// }
} else {
final pickedFile = await _picker.pickImage(source: ImageSource.gallery);
if (pickedFile != null) {
final imageFile = File(pickedFile.path);
setState(() {
if (isProfile) {
_profileImage = imageFile;
} else {
_headerImage = imageFile;
}
imageProvider =
kIsWeb ? MemoryImage(_profileImage) : FileImage(_profileImage);
});
} else {
imageProvider = AssetImage("assets/images/luffy.png");
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Point Saya"),
backgroundColor: Color.fromARGB(225, 79, 76, 182),
),
resizeToAvoidBottomInset: false,
backgroundColor: const Color.fromARGB(255, 255, 255, 255),
body: Stack(
children: [
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.only(top: 0.0, left: 20, right: 20),
child: Container(
height: MediaQuery.of(context).size.height * 0.7,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: const Color.fromARGB(150, 137, 123, 69),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
),
child: Column(
spacing: 10,
children: [
SizedBox(height: 4),
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 20,
vertical: 10,
),
decoration: BoxDecoration(
color: const Color.fromARGB(255, 191, 184, 184),
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(color: Colors.black12, blurRadius: 8),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(height: 5),
Row(
children: [
// Image.asset(
// 'assets/icons/diamond.png',
// width: 24,
// ),
Text(
"Total Point",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
SizedBox(width: 8),
Text(
"300",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
],
),
Row(
children: [
// Image.asset('assets/icons/coin.png', width: 24),
Text(
"Total Koin",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
SizedBox(width: 8),
Text(
"60",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
],
),
SizedBox(height: 5),
],
),
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(padding: EdgeInsets.all(8)),
Container(
width: MediaQuery.of(context).size.width * 0.8,
child: Center(
child: Text(
"Riwayat poin dan koin",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white60,
),
),
),
// color: Colors.amber,
),
],
),
PointsHeader(),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(width: 40),
Text("Furikake", style: TextStyle(color: Colors.white)),
SizedBox(width: 130),
Text(
"",
// "🔥 Level: ${points.level}",
// style: TextStyle(color: Colors.orangeAccent),
),
Text("100", style: TextStyle(color: Colors.lightBlue)),
SizedBox(width: 60),
Text("20", style: TextStyle(color: Colors.greenAccent)),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(width: 40),
Text("Furikake", style: TextStyle(color: Colors.white)),
SizedBox(width: 130),
Text(
"",
// "🔥 Level: ${points.level}",
// style: TextStyle(color: Colors.orangeAccent),
),
Text("100", style: TextStyle(color: Colors.lightBlue)),
SizedBox(width: 60),
Text("20", style: TextStyle(color: Colors.greenAccent)),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(width: 40),
Text("Furikake", style: TextStyle(color: Colors.white)),
SizedBox(width: 130),
Text(
"",
// "🔥 Level: ${points.level}",
// style: TextStyle(color: Colors.orangeAccent),
),
Text("100", style: TextStyle(color: Colors.lightBlue)),
SizedBox(width: 60),
Text("20", style: TextStyle(color: Colors.greenAccent)),
],
),
],
),
),
),
),
// Profile Picture (bisa diubah)
Positioned(
top: 25,
left: MediaQuery.of(context).size.width / 2 - 50,
child: GestureDetector(
onTap: () => _pickImage(true),
child: Stack(
alignment: Alignment.bottomRight,
children: [
CircleAvatar(
radius: 50,
backgroundColor: Colors.grey[300],
backgroundImage:
_profileImage != null
? (kIsWeb
? MemoryImage(_profileImage) as ImageProvider
: FileImage(_profileImage) as ImageProvider)
: null,
child:
_profileImage == null
? Image.asset("assets/images/luffy.png")
: null,
),
CircleAvatar(
radius: 15,
backgroundColor: Colors.blue,
child: Icon(Icons.edit, color: Colors.white, size: 15),
),
],
),
),
),
Positioned(
top: 130,
left: MediaQuery.of(context).size.width / 2 - 30,
child: GestureDetector(
onTap: () => _pickImage(true),
child: Stack(
alignment: Alignment.bottomRight,
children: [
Text(
"Luffy kun",
style: TextStyle(
color: Colors.deepPurple,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
],
),
);
}
Widget _buildListItem(IconData icon, String label, {bool isLogout = false}) {
return ListTile(
leading: Icon(
icon,
color:
isLogout
? const Color.fromARGB(255, 181, 47, 47)
: const Color.fromARGB(255, 255, 255, 255),
),
title: Text(label, style: TextStyle(fontSize: 16, color: Colors.black)),
trailing: Icon(Icons.arrow_forward_ios, size: 16),
onTap: () {},
);
}
}
+6 -4
View File
@@ -2,10 +2,10 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:furibase/components/bottom_navbar.dart';
import 'package:furibase/components/main_menu.dart';
import 'package:furibase/components/scan_button.dart';
import 'package:furibase/screen/saya/akun_saya.dart';
import 'package:freekake/components/bottom_navbar.dart';
import 'package:freekake/components/main_menu.dart';
import 'package:freekake/components/scan_button.dart';
import 'package:freekake/screen/saya/akun_saya.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
@@ -13,6 +13,8 @@ import 'package:image_picker_web/image_picker_web.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
class ProfileScreenCopy extends StatefulWidget {
const ProfileScreenCopy({super.key});
@override
_ProfileScreenState createState() => _ProfileScreenState();
}
+19 -3
View File
@@ -1,18 +1,22 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
// import 'package:flutter_html/flutter_html.dart';
import 'package:freekake/components/bottom_navbar.dart';
import 'package:freekake/components/main_menu.dart';
import 'package:freekake/components/scan_button.dart';
import 'package:freekake/components/topbar_container.dart';
import 'package:freekake/screen/saya/akun_saya.dart';
import 'package:freekake/screen/saya/data_saya.dart';
import 'package:freekake/screen/saya/point_saya.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
import 'package:flutter/foundation.dart' show kIsWeb;
class ProfileScreen extends StatefulWidget {
const ProfileScreen({super.key});
@override
_ProfileScreenState createState() => _ProfileScreenState();
}
@@ -212,12 +216,24 @@ class _ProfileScreenState extends State<ProfileScreen> {
padding: const EdgeInsets.symmetric(horizontal: 25.0),
child: Divider(height: 0),
),
_buildListItem(Icons.person, "Data Diri", () => {}),
_buildListItem(Icons.person, "Data Diri", () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DataSaya()),
);
}),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25.0),
child: Divider(height: 0),
),
_buildListItem(Icons.person, "Point Saya", () => {}),
_buildListItem(Icons.person, "Point Saya", () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PointSaya(),
),
);
}),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 25.0,
+31
View File
@@ -0,0 +1,31 @@
class PlayerPoints {
int xp;
int gp;
int sp;
int level;
PlayerPoints({
required this.xp,
required this.gp,
required this.sp,
required this.level,
});
void addXP(int value) {
xp += value;
if (xp >= xpToNextLevel()) {
levelUp();
}
}
void addGP(int value) => gp += value;
void addSP(int value) => sp += value;
int xpToNextLevel() => 1000 + (level * 200);
void levelUp() {
level++;
sp++;
xp = 0;
}
}
+17 -1
View File
@@ -822,7 +822,23 @@ packages:
sha256: "18b1640839cf6546784a524c72aded5b6e86b23e7167dc2311cc96f7658b64bd"
url: "https://pub.dev"
source: hosted
version: "2.11.0"
version: "2.10.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:
dependency: transitive
description:
name: webview_flutter_web
sha256: "18a7ccc1c31dd9a5c759a1b7217a2a1e04bd8f65712714a4070bfac19a23ca9e"
url: "https://pub.dev"
source: hosted
version: "0.2.3+4"
webview_flutter_wkwebview:
dependency: "direct main"
description:
+4
View File
@@ -94,6 +94,10 @@ flutter:
# SVG files
- assets/icons/
- assets/html/
- assets/html/furikake/
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
+1 -1
View File
@@ -8,7 +8,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:furibase/main.dart';
import 'package:freekake/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {