Compare commits

..
9 Commits
Author SHA1 Message Date
='fauz 9bff9c7fc2 game html dan DIO 2025-07-07 17:31:26 +07:00
='fauz a211b0675d html content 2025-07-04 16:04:09 +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
='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
94 changed files with 3097 additions and 1949 deletions
+1 -9
View File
@@ -43,12 +43,4 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
# Place unity project here
/unity
# flutter will place unity Library dependency here
/unityLibrary
# unityLibrary will place build here
/android/unityLibrary
pubspec.lock
+10 -233
View File
@@ -1,239 +1,16 @@
# Furikake App
# furibase
Project ini adalah aplikasi mobile Furikake.
A new Flutter project.
## Konfiguras dan perubahan android app
## Getting Started
Edit android/app/src/main/kotlin ... /MainActivity.kt
This project is a starting point for a Flutter application.
```
package com.paj.freekake
A few resources to get you started if this is your first Flutter project:
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.android.FlutterFragmentActivity;
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
//class MainActivity : FlutterActivity()
class MainActivity: FlutterFragmentActivity() {
}
```
Gunakan konfigurasi ini pada file android/settings.gradle.kts
```
pluginManagement {
val flutterSdkPath = run {
val properties = java.util.Properties()
file("local.properties").inputStream().use { properties.load(it) }
val flutterSdkPath = properties.getProperty("flutter.sdk")
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
flutterSdkPath
}
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.9.0" apply false
id("org.jetbrains.kotlin.android") version "1.8.22" apply false
}
include(":app")
include(":unityLibrary")
project(":unityLibrary").projectDir = file("./unityLibrary")
include(":unityLibrary:xrmanifest.androidlib")
//project(":unityLibrary:xrmanifest.androidlib").projectDir = file("unityLibrary/xrmanifest.androidlib")
//include(":unityLibrary:mobilenotifications.androidlib")
//project(":unityLibrary:mobilenotifications.androidlib").projectDir = file("unityLibrary/mobilenotifications.androidlib")
```
Gunakan konfigurasi ini pada android/app/build.gradle.kts
```
plugins {
id("com.android.application")
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
}
android {
namespace = "com.paj.freekake"
//compileSdk = flutter.compileSdkVersion
compileSdk = 35
//ndkVersion = "27.0.12077973"
//ndkVersion = flutter.ndkVersion
ndkVersion = "23.1.7779620"
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.paj.freekake"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
//minSdk = flutter.minSdkVersion
//targetSdk = flutter.targetSdkVersion
//minSdk = 24 // Set to 24 or higher
minSdk = 30
targetSdk = 35
versionCode = flutter.versionCode
versionName = flutter.versionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
}
}
}
flutter {
source = "../.."
}
dependencies {
implementation(project(":unityLibrary"))
implementation(project(":flutter_unity_widget"))
}
```
## Konfigurasi UnityLibrary
Gunakan konfigurasi ini pada file android/unityLibrary/build.gradle:
```
apply plugin: 'com.android.library'
dependencies {
implementation(name: 'unity-classes', ext:'jar')
implementation(name: 'arcore_client', ext:'aar')
implementation(name: 'ARPresto', ext:'aar')
implementation(name: 'UnityARCore', ext:'aar')
implementation(name: 'unityandroidpermissions', ext:'aar')
implementation project('xrmanifest.androidlib')
//implementation project('mobilenotifications.androidlib')
}
android {
namespace "com.unity3d.player"
// ndkPath "/Applications/Unity/Hub/Editor/2022.3.60f1/PlaybackEngines/AndroidPlayer/NDK"
compileSdkVersion 35
buildToolsVersion '34.0.0'
ndkVersion "23.1.7779620"
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
defaultConfig {
minSdkVersion 30
targetSdkVersion 35
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
versionCode 1
versionName '1.0.2'
consumerProguardFiles 'proguard-unity.txt'
}
lintOptions {
abortOnError false
}
aaptOptions {
noCompress = ['.unity3d', '.ress', '.resource', '.obb', '.bundle', '.unityexp']
ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~"
}
packagingOptions {
doNotStrip '*/armeabi-v7a/*.so'
doNotStrip '*/arm64-v8a/*.so'
jniLibs {
useLegacyPackaging true
}
}
}
def getSdkDir() {
Properties local = new Properties()
local.load(new FileInputStream("${rootDir}/local.properties"))
return local.getProperty('sdk.dir')
}
def BuildIl2Cpp(String workingDir, String configuration, String architecture, String abi, String[] staticLibraries) {
def commandLineArgs = []
commandLineArgs.add("--compile-cpp")
commandLineArgs.add("--platform=Android")
commandLineArgs.add("--architecture=" + architecture)
commandLineArgs.add("--outputpath=" + workingDir + "/src/main/jniLibs/" + abi + "/libil2cpp.so")
commandLineArgs.add("--baselib-directory=" + workingDir + "/src/main/jniStaticLibs/" + abi)
commandLineArgs.add("--incremental-g-c-time-slice=3")
commandLineArgs.add("--dotnetprofile=unityaot-linux")
commandLineArgs.add("--enable-debugger")
commandLineArgs.add("--profiler-report")
commandLineArgs.add("--profiler-output-file=" + workingDir + "/build/il2cpp_"+ abi + "_" + configuration + "/il2cpp_conv.traceevents")
commandLineArgs.add("--print-command-line")
commandLineArgs.add("--data-folder=" + workingDir + "/src/main/Il2CppOutputProject/Source/il2cppOutput/data")
commandLineArgs.add("--generatedcppdir=" + workingDir + "/src/main/Il2CppOutputProject/Source/il2cppOutput")
commandLineArgs.add("--cachedirectory=" + workingDir + "/build/il2cpp_"+ abi + "_" + configuration + "/il2cpp_cache")
commandLineArgs.add("--tool-chain-path=" + android.ndkDirectory)
staticLibraries.eachWithIndex {fileName, i->
commandLineArgs.add("--additional-libraries=" + workingDir + "/src/main/jniStaticLibs/" + abi + "/" + fileName)
}
def executableExtension = ""
if (org.gradle.internal.os.OperatingSystem.current().isWindows()) {
executableExtension = ".exe"
commandLineArgs = commandLineArgs*.replace('\"', '\\\"')
}
exec {
executable workingDir + "/src/main/Il2CppOutputProject/IL2CPP/build/deploy/il2cpp" + executableExtension
args commandLineArgs
environment "ANDROID_SDK_ROOT", getSdkDir()
}
delete workingDir + "/src/main/jniLibs/" + abi + "/libil2cpp.sym.so"
ant.move(file: workingDir + "/src/main/jniLibs/" + abi + "/libil2cpp.dbg.so", tofile: workingDir + "/symbols/" + abi + "/libil2cpp.so")
}
android {
task BuildIl2CppTask {
doLast {
BuildIl2Cpp(projectDir.toString().replaceAll('\\\\', '/'), 'Debug', 'armv7', 'armeabi-v7a', [ ] as String[]);
BuildIl2Cpp(projectDir.toString().replaceAll('\\\\', '/'), 'Debug', 'arm64', 'arm64-v8a', [ ] as String[]);
}
}
afterEvaluate {
if (project(':unityLibrary').tasks.findByName('mergeDebugJniLibFolders'))
project(':unityLibrary').mergeDebugJniLibFolders.dependsOn BuildIl2CppTask
if (project(':unityLibrary').tasks.findByName('mergeReleaseJniLibFolders'))
project(':unityLibrary').mergeReleaseJniLibFolders.dependsOn BuildIl2CppTask
}
sourceSets {
main {
jni.srcDirs = ["src/main/Il2CppOutputProject"]
}
}
}
```
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
+6 -13
View File
@@ -9,28 +9,26 @@ android {
namespace = "com.example.furibase"
//compileSdk = flutter.compileSdkVersion
compileSdk = 35
//ndkVersion = "27.0.12077973"
ndkVersion = "27.0.12077973"
//ndkVersion = flutter.ndkVersion
ndkVersion = "23.1.7779620"
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
jvmTarget = JavaVersion.VERSION_11.toString()
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.paj.freekake"
applicationId = "com.example.furibase"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
//minSdk = flutter.minSdkVersion
//targetSdk = flutter.targetSdkVersion
//minSdk = 24 // Set to 24 or higher
minSdk = 30
minSdk = 24 // Set to 24 or higher
targetSdk = 35
versionCode = flutter.versionCode
versionName = flutter.versionName
@@ -52,8 +50,3 @@ android {
flutter {
source = "../.."
}
dependencies {
implementation(project(":unityLibrary"))
implementation(project(":flutter_unity_widget"))
}
+2
View File
@@ -2,6 +2,8 @@
<application
android:label="furibase"
android:name="${applicationName}"
android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
@@ -1,8 +1,5 @@
package com.example.furibase
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.android.FlutterFragmentActivity;
//class MainActivity : FlutterActivity()
class MainActivity: FlutterFragmentActivity() {
}
class MainActivity : FlutterActivity()
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true"/>
</network-security-config>
-39
View File
@@ -1,11 +1,5 @@
allprojects {
repositories {
flatDir {
dirs(
file("${project(":unityLibrary").projectDir}/libs")
)
}
google()
mavenCentral()
}
@@ -18,39 +12,6 @@ subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
afterEvaluate {
if (project.extensions.findByName("android") != null) {
extensions.configure<com.android.build.gradle.BaseExtension>("android") {
if (namespace == null) {
namespace = project.group.toString()
}
}
}
if (plugins.hasPlugin("com.android.application") || plugins.hasPlugin("com.android.library")) {
if (name == "flutter_unity_widget") {
extensions.configure<com.android.build.gradle.BaseExtension>("android") {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
(this as ExtensionAware).extensions.configure<org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions>("kotlinOptions") {
jvmTarget = "17"
}
if (namespace == null) {
namespace = project.group.toString()
}
compileSdkVersion(35)
}
}
}
}
}
subprojects {
project.evaluationDependsOn(":app")
}
+1 -2
View File
@@ -1,4 +1,3 @@
# org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
+1 -23
View File
@@ -18,30 +18,8 @@ pluginManagement {
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.9.0" apply false
id("com.android.application") version "8.7.0" apply false
id("org.jetbrains.kotlin.android") version "1.8.22" apply false
}
include(":app")
include(":unityLibrary")
project(":unityLibrary").projectDir = file("./unityLibrary")
include(":unityLibrary:xrmanifest.androidlib")
// project(":unityLibrary:xrmanifest.androidlib").projectDir = file("unityLibrary/xrmanifest.androidlib")
// include(":unityLibrary:mobilenotifications.androidlib")
// project(":unityLibrary:mobilenotifications.androidlib").projectDir = file("unityLibrary/mobilenotifications.androidlib")
include(":unityLibrary")
project(":unityLibrary").projectDir = file("./unityLibrary")
include(":unityLibrary")
project(":unityLibrary").projectDir = file("./unityLibrary")
include(":unityLibrary")
project(":unityLibrary").projectDir = file("./unityLibrary")
-117
View File
@@ -1,117 +0,0 @@
apply plugin: 'com.android.library'
dependencies {
implementation(name: 'unity-classes', ext:'jar')
implementation(name: 'arcore_client', ext:'aar')
implementation(name: 'ARPresto', ext:'aar')
implementation(name: 'UnityARCore', ext:'aar')
implementation(name: 'unityandroidpermissions', ext:'aar')
implementation project('xrmanifest.androidlib')
//implementation project('mobilenotifications.androidlib')
}
android {
namespace "com.unity3d.player"
// ndkPath "/Applications/Unity/Hub/Editor/2022.3.60f1/PlaybackEngines/AndroidPlayer/NDK"
compileSdkVersion 35
buildToolsVersion '34.0.0'
ndkVersion "23.1.7779620"
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
defaultConfig {
minSdkVersion 30
targetSdkVersion 35
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
versionCode 1
versionName '1.0.2'
consumerProguardFiles 'proguard-unity.txt'
}
lintOptions {
abortOnError false
}
aaptOptions {
noCompress = ['.unity3d', '.ress', '.resource', '.obb', '.bundle', '.unityexp']
ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~"
}
packagingOptions {
doNotStrip '*/armeabi-v7a/*.so'
doNotStrip '*/arm64-v8a/*.so'
jniLibs {
useLegacyPackaging true
}
}
}
def getSdkDir() {
Properties local = new Properties()
local.load(new FileInputStream("${rootDir}/local.properties"))
return local.getProperty('sdk.dir')
}
def BuildIl2Cpp(String workingDir, String configuration, String architecture, String abi, String[] staticLibraries) {
def commandLineArgs = []
commandLineArgs.add("--compile-cpp")
commandLineArgs.add("--platform=Android")
commandLineArgs.add("--architecture=" + architecture)
commandLineArgs.add("--outputpath=" + workingDir + "/src/main/jniLibs/" + abi + "/libil2cpp.so")
commandLineArgs.add("--baselib-directory=" + workingDir + "/src/main/jniStaticLibs/" + abi)
commandLineArgs.add("--incremental-g-c-time-slice=3")
commandLineArgs.add("--dotnetprofile=unityaot-linux")
commandLineArgs.add("--enable-debugger")
commandLineArgs.add("--profiler-report")
commandLineArgs.add("--profiler-output-file=" + workingDir + "/build/il2cpp_"+ abi + "_" + configuration + "/il2cpp_conv.traceevents")
commandLineArgs.add("--print-command-line")
commandLineArgs.add("--data-folder=" + workingDir + "/src/main/Il2CppOutputProject/Source/il2cppOutput/data")
commandLineArgs.add("--generatedcppdir=" + workingDir + "/src/main/Il2CppOutputProject/Source/il2cppOutput")
commandLineArgs.add("--cachedirectory=" + workingDir + "/build/il2cpp_"+ abi + "_" + configuration + "/il2cpp_cache")
commandLineArgs.add("--tool-chain-path=" + android.ndkDirectory)
staticLibraries.eachWithIndex {fileName, i->
commandLineArgs.add("--additional-libraries=" + workingDir + "/src/main/jniStaticLibs/" + abi + "/" + fileName)
}
def executableExtension = ""
if (org.gradle.internal.os.OperatingSystem.current().isWindows()) {
executableExtension = ".exe"
commandLineArgs = commandLineArgs*.replace('\"', '\\\"')
}
exec {
executable workingDir + "/src/main/Il2CppOutputProject/IL2CPP/build/deploy/il2cpp" + executableExtension
args commandLineArgs
environment "ANDROID_SDK_ROOT", getSdkDir()
}
delete workingDir + "/src/main/jniLibs/" + abi + "/libil2cpp.sym.so"
ant.move(file: workingDir + "/src/main/jniLibs/" + abi + "/libil2cpp.dbg.so", tofile: workingDir + "/symbols/" + abi + "/libil2cpp.so")
}
android {
task BuildIl2CppTask {
doLast {
BuildIl2Cpp(projectDir.toString().replaceAll('\\\\', '/'), 'Debug', 'armv7', 'armeabi-v7a', [ ] as String[]);
BuildIl2Cpp(projectDir.toString().replaceAll('\\\\', '/'), 'Debug', 'arm64', 'arm64-v8a', [ ] as String[]);
}
}
afterEvaluate {
if (project(':unityLibrary').tasks.findByName('mergeDebugJniLibFolders'))
project(':unityLibrary').mergeDebugJniLibFolders.dependsOn BuildIl2CppTask
if (project(':unityLibrary').tasks.findByName('mergeReleaseJniLibFolders'))
project(':unityLibrary').mergeReleaseJniLibFolders.dependsOn BuildIl2CppTask
}
sourceSets {
main {
jni.srcDirs = ["src/main/Il2CppOutputProject"]
}
}
}
+180
View File
@@ -0,0 +1,180 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Game Nusantara</title>
<style>
body {
font-family: sans-serif;
margin: 0;
background: #f2f2f2;
}
.container {
max-width: 480px;
margin: auto;
padding: 1rem;
}
h1 {
text-align: center;
font-size: 1.4rem;
}
.grid {
display: grid;
gap: 4px;
margin-top: 1rem;
}
.letter {
background: white;
border-radius: 6px;
padding: 0;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
display: flex;
justify-content: center;
align-items: center;
font-size: 1rem;
font-weight: bold;
text-align: center;
user-select: none;
cursor: pointer;
border: 1px solid #ccc;
aspect-ratio: 1/1;
}
.found {
background-color: #a2f5a2 !important;
}
</style>
</head>
<body>
<div class="container">
<h1 id="game-title">Game Nusantara</h1>
<div id="game-board"></div>
<h3>Kata yang harus ditemukan:</h3>
<ul id="target-words" style="padding-left: 1rem;"></ul>
<p id="penjelasan" style="margin-top: 1rem; font-size: 0.9rem; color: #444;"></p>
</div>
<script>
// Template Find The Word Game (drag to select)
const words = ['TENANG', 'SABAR', 'SYUKUR', 'CINTA', 'GEMBIRA', 'BANGGA'];
const board = document.getElementById('game-board');
const size = 12; // 12x12 grid
let grid = Array(size * size).fill('');
function randomLetter() {
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
return alphabet[Math.floor(Math.random() * alphabet.length)];
}
function placeWord(word) {
const dir = Math.random() < 0.5 ? 'H' : 'V';
let x, y, success = false;
while (!success) {
x = Math.floor(Math.random() * (dir === 'H' ? size - word.length : size));
y = Math.floor(Math.random() * (dir === 'V' ? size - word.length : size));
let fits = true;
for (let i = 0; i < word.length; i++) {
let idx = dir === 'H' ? y * size + (x + i) : (y + i) * size + x;
if (grid[idx] && grid[idx] !== word[i]) {
fits = false;
break;
}
}
if (fits) {
for (let i = 0; i < word.length; i++) {
let idx = dir === 'H' ? y * size + (x + i) : (y + i) * size + x;
grid[idx] = word[i];
}
success = true;
}
}
}
words.forEach(w => placeWord(w));
grid = grid.map(cell => cell || randomLetter());
function renderGrid() {
board.className = 'grid';
board.style.gridTemplateColumns = `repeat(${size}, 1fr)`;
board.innerHTML = '';
grid.forEach((char, i) => {
const div = document.createElement('div');
div.textContent = char;
div.className = 'letter';
div.dataset.index = i;
div.style.aspectRatio = '1/1';
div.style.border = '1px solid #ccc';
div.style.display = 'flex';
div.style.alignItems = 'center';
div.style.justifyContent = 'center';
div.style.fontSize = '1.2rem';
div.style.fontWeight = 'bold';
div.style.userSelect = 'none';
div.style.cursor = 'pointer';
board.appendChild(div);
});
}
renderGrid();
// Daftar kata yang harus ditemukan
// (Daftar kata tidak lagi ditampilkan sebagai list)
let currentSelection = [];
let currentWord = '';
let wordsFound = new Set();
let isMouseDown = false;
board.addEventListener('mousedown', (e) => {
if (!e.target.classList.contains('letter')) return;
isMouseDown = true;
selectCell(e.target);
});
board.addEventListener('mouseover', (e) => {
if (!isMouseDown || !e.target.classList.contains('letter')) return;
selectCell(e.target);
});
document.addEventListener('mouseup', () => {
if (words.includes(currentWord)) {
currentSelection.forEach(c => {
c.style.backgroundColor = '#a2f5a2';
c.classList.add('found');
});
const penjelasanBox = document.getElementById('penjelasan');
const regex = new RegExp(currentWord, 'gi');
penjelasanBox.innerHTML = penjelasanBox.innerHTML.replace(regex, `<span style="color:green; font-weight:bold">${currentWord}</span>`);
wordsFound.add(currentWord);
if (wordsFound.size === words.length) {
alert('Selamat! Kamu telah menemukan semua kata positif. Ingat, berpikir positif setiap hari bisa membuat hidupmu lebih ceria dan penuh semangat 🌈');
}
} else {
currentSelection.forEach(c => c.style.backgroundColor = '');
}
currentSelection = [];
currentWord = '';
isMouseDown = false;
});
function selectCell(cell) {
if (cell.classList.contains('found') || currentSelection.includes(cell)) return;
cell.style.backgroundColor = '#ffeaa7';
currentSelection.push(cell);
currentWord += cell.textContent;
}
// Penjelasan pentingnya emosi positif
const penjelasan = `Di dalam hidup, kita bisa merasa banyak hal. Tapi tahukah kamu? Emosi positif seperti <strong>tenang</strong>, <strong>sabar</strong>, <strong>syukur</strong>, <strong>cinta</strong>, <strong>gembira</strong>, dan <strong>bangga</strong> adalah kunci untuk hidup yang sehat dan bahagia. Yuk, temukan kata-kata positif itu di puzzle, lalu pikirkan kapan terakhir kali kamu merasakannya!`;
document.getElementById('penjelasan').innerHTML = penjelasan;
function includeScript(file) {
const script = document.createElement('script');
script.src = file;
document.body.appendChild(script);
}
</script>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

+16
View File
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test</title>
<script>
window.onload = function() {
document.body.innerHTML += '<p style="color:green;">✅ JS is working!</p>';
};
window.onload = showAlert;
</script>
</head>
<body>
<h1>Hello WebView</h1>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

+15 -15
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,21 +222,21 @@ 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">
<img src="assets/html/furikake/nori.jpeg" alt="Nori">
<div class="div-grid">
<div class="div-item">
<img src="nori.jpeg" alt="Nori">
<p>Nori (Rumput Laut)</p>
</div>
<div class="bahan-item">
<img src="assets/html/furikake/wijen.jpeg" alt="Biji Wijen">
<div class="div-item">
<img src="wijen.jpeg" alt="Biji Wijen">
<p>Biji Wijen</p>
</div>
<div class="bahan-item">
<img src="assets/html/furikake/ikankering.jpg" alt="Katsuobushi">
<div class="div-item">
<img src="ikankering.jpg" alt="Katsuobushi">
<p>Katsuobushi (Ikan Kering)</p>
</div>
<div class="bahan-item">
<img src="assets/html/furikake/gulagaram.jpg" alt="Garam dan Gula">
<div class="div-item">
<img src="gulagaram.jpg" alt="Garam dan Gula">
<p>Garam & Gula</p>
</div>
</div>
+17 -17
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,25 +175,25 @@
<h2>Apa Itu Gizi Seimbang?</h2>
<p>Gizi seimbang adalah cara makan yang mengandung semua zat gizi yang dibutuhkan tubuh. Zat gizi itu terdiri dari:</p>
<div class="gizi-grid">
<div class="gizi-item">
<img src="assets/html/karbohidrat.png" alt="Karbohidrat" />
<div class="div-grid">
<div class="div-item">
<img src="karbohidrat.png" alt="Karbohidrat" />
<p><strong>Karbohidrat</strong><br>Sumber energi seperti nasi, roti, dan kentang.</p>
</div>
<div class="gizi-item">
<img src="assets/html/protein.png" alt="Protein" />
<div class="div-item">
<img src="protein.png" alt="Protein" />
<p><strong>Protein</strong><br>Untuk membangun otot, misalnya ikan, telur, dan tahu.</p>
</div>
<div class="gizi-item">
<img src="assets/html/lemak.png" alt="Lemak" />
<div class="div-item">
<img src="lemak.png" alt="Lemak" />
<p><strong>Lemak</strong><br>Memberikan tenaga, seperti dari kacang-kacangan dan minyak sehat.</p>
</div>
<div class="gizi-item">
<img src="assets/html/vitaminmineral.png" alt="Vitamin dan Mineral" />
<div class="div-item">
<img src="vitaminmineral.png" alt="Vitamin dan Mineral" />
<p><strong>Vitamin dan mineral</strong><br>Dari sayur dan buah yang membantu tubuh bekerja dengan baik.</p>
</div>
<div class="gizi-item">
<img src="assets/html/seratair.png" alt="Serat dan Air" />
<div class="div-item">
<img src="seratair.png" alt="Serat dan Air" />
<p><strong>Serat dan air</strong><br>Membantu pencernaan dan menjaga tubuh tetap segar!</p>
</div>
</div>
@@ -209,7 +209,7 @@
<li>🍽️ Seperempat piring: Protein (seperti ayam atau tempe)</li>
<li>🍽️ Sedikit lemak sehat</li>
</ul>
<img src="assets/html/isipiringku.png" alt="Piring Gizi Seimbang" class="full-width-image">
<img src="isipiringku.png" alt="Piring Gizi Seimbang" class="full-width-image">
<figcaption>Isi Piringku</figcaption>
<p>Selain itu, kita tidak boleh makan terlalu banyak atau terlalu sedikit, karena bisa membuat tubuh tidak sehat. Makan berlebihan bisa menyebabkan obesitas, sedangkan makan terlalu sedikit bisa membuat tubuh lemas dan kurang gizi.</p>
</section>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

+176
View File
@@ -0,0 +1,176 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Game Nusantara</title>
<link rel="icon" href="data:,">
<style>
body {
font-family: sans-serif;
margin: 0;
background: #f2f2f2;
}
.container {
max-width: 480px;
margin: auto;
padding: 1rem;
}
h1 {
text-align: center;
font-size: 1.4rem;
}
.grid, .drag-area, .drop-area {
display: grid;
gap: 10px;
margin-top: 1rem;
}
.grid {
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
}
.card, .letter, .drop-zone, .drag-item {
background: white;
border-radius: 6px;
padding: 10px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
display: flex;
justify-content: center;
align-items: center;
font-size: 0.8rem;
text-align: center;
user-select: none;
}
.hidden { background: #ccc; color: transparent; }
.matched { background: #d4edda; }
.drop-zone { height: 80px; background: #eee; border: 2px dashed #bbb; }
.drag-item[draggable="true"] { cursor: grab; }
</style>
</head>
<body>
<div class="container">
<h1 id="game-title">Game Nusantara</h1>
<div id="game-board"></div>
</div>
<script>
const items = [
'pempek.png', 'sumsel.png',
'sopkonro.png', 'sulsel.png',
'rendang.png', 'sumbar.png',
'tinutuan.png', 'sulut.png',
'ayambetutu.png', 'bali.png',
'bikaambon.png', 'sumut.png',
'gudeg.png', 'yogyakarta.png',
'sotobanjar.png', 'kalsel.png',
'ayamtaliwang.png', 'ntb.png'
];
const board = document.getElementById('game-board');
board.className = 'grid';
board.style.gridTemplateColumns = 'repeat(3, 1fr)';
let selectedItems = shuffle([...items]);
let shuffled = shuffle(selectedItems.slice(0, 18));
let selected = [], matched = [];
function shuffle(array) {
return array.sort(() => Math.random() - 0.5);
}
function render() {
board.innerHTML = '';
shuffled.forEach((img, i) => {
const card = document.createElement('div');
card.className = 'card hidden';
card.style.backgroundImage = 'url("../freekake.png")';
card.style.backgroundSize = 'contain';
card.style.backgroundRepeat = 'no-repeat';
card.style.backgroundPosition = 'center';
card.dataset.index = i;
card.style.aspectRatio = '1/1';
card.style.borderRadius = '12px';
card.style.overflow = 'hidden';
card.style.backgroundColor = '#fff';
card.style.display = 'flex';
card.style.flexDirection = 'column';
card.style.alignItems = 'center';
card.style.justifyContent = 'center';
card.style.padding = '8px';
card.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
board.appendChild(card);
});
}
function revealCard(i) {
const el = board.children[i];
el.classList.remove('hidden');
el.style.backgroundImage = 'none';
el.innerHTML = `<img src="${shuffled[i]}" alt="" style="width: 100%; height: auto; object-fit: contain;">`;
}
function hideCard(i) {
const el = board.children[i];
el.classList.add('hidden');
el.style.backgroundImage = 'url("../freekake.png")';
el.style.backgroundSize = 'contain';
el.style.backgroundRepeat = 'no-repeat';
el.style.backgroundPosition = 'center';
el.innerHTML = '';
}
function checkMatch() {
const [a, b] = selected;
const pairs = {
'pempek.png': 'sumsel.png', 'sumsel.png': 'pempek.png',
'sopkonro.png': 'sulsel.png', 'sulsel.png': 'sopkonro.png',
'rendang.png': 'sumbar.png', 'sumbar.png': 'rendang.png',
'tinutuan.png': 'sulut.png', 'sulut.png': 'tinutuan.png',
'ayambetutu.png': 'bali.png', 'bali.png': 'ayambetutu.png',
'bikaambon.png': 'sumut.png', 'sumut.png': 'bikaambon.png',
'gudeg.png': 'yogyakarta.png', 'yogyakarta.png': 'gudeg.png',
'sotobanjar.png': 'kalsel.png', 'kalsel.png': 'sotobanjar.png',
'ayamtaliwang.png': 'ntb.png', 'ntb.png': 'ayamtaliwang.png'
};
if (pairs[shuffled[a]] === shuffled[b]) {
matched.push(a, b);
board.children[a].classList.add('matched');
board.children[a].style.backgroundColor = '#d4edda';
board.children[b].classList.add('matched');
board.children[b].style.backgroundColor = '#d4edda';
} else {
setTimeout(() => {
hideCard(a);
hideCard(b);
}, 800);
}
selected = [];
}
render();
board.addEventListener('click', (e) => {
const index = e.target.closest('.card')?.dataset.index;
if (!index || selected.includes(+index) || matched.includes(+index)) return;
revealCard(index);
selected.push(+index);
if (selected.length === 2) checkMatch();
});
window.render = render;
if (window.flutter_inappwebview) {
window.flutter_inappwebview.callHandler('pageReady', {
boardExists: !!document.getElementById("game-board"),
hasRender: typeof render === "function"
});
}
// Optional helper
function includeScript(file) {
const script = document.createElement('script');
script.src = file;
document.body.appendChild(script);
}
</script>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

+2 -5
View File
@@ -1,9 +1,5 @@
import 'package:flutter/material.dart';
import 'package:freekake/components/navbar_container.dart';
import 'package:freekake/components/navbar_shape_1.dart';
import 'package:freekake/components/navbar_shape_2.dart';
import 'package:freekake/components/navbar_shape_3.dart';
import 'package:freekake/components/custom_shape.dart';
import 'package:flutter/material.dart';
class BottomNavbar extends StatefulWidget {
const BottomNavbar({super.key});
@@ -13,6 +9,7 @@ class BottomNavbar extends StatefulWidget {
}
class _BottomNavbarState extends State<BottomNavbar> {
@override
Widget build(BuildContext context) {
final Size screenSize = MediaQuery.of(context).size;
return CustomPaint(
+79 -7
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,7 +33,15 @@ class CardList extends StatelessWidget {
borderRadius: BorderRadius.circular(
10.0,
), // Opsional: membuat gambar lebih rounded
child: Image.asset(
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,
@@ -34,7 +54,7 @@ class CardList extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
this.title,
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
@@ -54,18 +74,70 @@ class CardList extends StatelessWidget {
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
+22 -57
View File
@@ -1,21 +1,15 @@
import 'package:freekake/components/menu_button.dart';
import 'package:freekake/providers/menu_selection_provider.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';
import 'package:flutter/material.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';
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';
import 'package:provider/provider.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(
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:
() => {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HomeScreen()),
),
},
onPress: () => _onItemTapped(context, 0),
isSelected: selectedIndex == 0,
),
MenuButton(
label: "Koleksi",
icon: 'assets/icons/Koleksi.svg',
onPress:
() => {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => KoleksiScreen()),
),
},
onPress: () => _onItemTapped(context, 1),
isSelected: selectedIndex == 1,
),
SizedBox(width: 100),
MenuButton(
label: "Pustaka",
icon: 'assets/icons/Pustaka.svg',
onPress:
() => {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PustakaScreen()),
),
},
onPress: () => _onItemTapped(context, 2),
isSelected: selectedIndex == 2,
),
MenuButton(
label: "Saya",
icon: 'assets/icons/Saya.svg',
onPress:
() => {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ProfileScreen()),
),
},
onPress: () => _onItemTapped(context, 3),
isSelected: selectedIndex == 3,
),
],
),
);
}
+2 -3
View File
@@ -28,8 +28,8 @@ class MenuButton extends StatelessWidget {
IconButton(
icon: SvgPicture.asset(
icon ?? '',
width: w ?? 24,
height: h ?? 24,
width: w ?? 28,
height: h ?? 28,
// allowDrawingOutsideViewBox: true,
colorFilter: ColorFilter.mode(
(isSelected ?? false)
@@ -48,7 +48,6 @@ class MenuButton extends StatelessWidget {
? Color.fromARGB(255, 216, 182, 10)
: Color.fromARGB(255, 239, 224, 232),
fontWeight: FontWeight.bold,
fontSize: 10,
),
),
],
+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),
),
],
),
);
}
}
+3 -2
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';
// 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(
-14
View File
@@ -1,14 +0,0 @@
import 'dart:io';
import 'package:image_picker/image_picker.dart';
class ImagePickerHelper {
Future<dynamic> pickImage() async {
final ImagePicker picker = ImagePicker();
final XFile? pickedFile = await picker.pickImage(source: ImageSource.gallery);
if (pickedFile != null) {
File imageFile = File(pickedFile.path);
return imageFile;
}
}
}
-5
View File
@@ -1,5 +0,0 @@
class ImagePickerHelper {
Future<void> pickImage() async {
throw UnsupportedError('pickImage is not supported on this platform');
}
}
-35
View File
@@ -1,35 +0,0 @@
// import 'package:image_picker_web/image_picker_web.dart';
// import 'dart:typed_data';
// class ImagePickerHelper {
// Future<dynamic> pickImage() async {
// Uint8List? bytesFromPicker = await ImagePickerWeb.getImageAsBytes();
// if (bytesFromPicker != null) {
// return bytesFromPicker;
// }
// }
// }
import 'dart:typed_data';
import 'dart:html' as html;
class ImagePickerHelper {
Future<void> pickImage() async {
html.FileUploadInputElement uploadInput = html.FileUploadInputElement();
uploadInput.accept = 'image/*';
uploadInput.click();
uploadInput.onChange.listen((event) async {
final file = uploadInput.files!.first;
final reader = html.FileReader();
reader.readAsArrayBuffer(file);
reader.onLoadEnd.listen((event) {
Uint8List imageBytes = reader.result as Uint8List;
if (imageBytes != null) {
return imageBytes;
}
});
});
}
}
-3
View File
@@ -1,3 +0,0 @@
export 'image_picker_stub.dart'
if (dart.library.html) 'image_picker_web.dart'
if (dart.library.io) 'image_picker_mobile.dart';
+5 -4
View File
@@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:freekake/components/navbar_container.dart';
import 'package:flutter/services.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();
@@ -13,7 +13,7 @@ void main() async {
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown, // bisa dihapus jika hanya ingin satu arah
DeviceOrientation.portraitDown,
]);
runApp(const MyApp());
@@ -28,10 +28,11 @@ class MyApp extends StatelessWidget {
providers: [
ChangeNotifierProvider(create: (context) => CharacterProvider()),
ChangeNotifierProvider(create: (context) => MenuSelectionProvider()),
ChangeNotifierProvider(create: (_) => PointsProvider()),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Freekake',
title: 'AR Character App',
theme: ThemeData.dark(),
home:
// DrawScreen(),
+31
View File
@@ -0,0 +1,31 @@
class Users {
int id;
// String nama;
// String tgl;
// String? alamat;
Users({
required this.id,
// required this.nama, this.alamat, required this.tgl
});
Map<String, dynamic> toMap() {
return {
"id": id,
// "nama": nama, "alamat": this.alamat, "tgl": this.tgl
};
}
factory Users.fromJson(Map<String, dynamic> map) {
return Users(
id: map['id'],
// nama: map['nama'],
// alamat: map['alamat'],
// tgl: map['tanggal'],
);
}
String toString() {
return 'Users{id:$id,}';
}
}
+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();
}
}
+142 -255
View File
@@ -3,22 +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;
@override
Widget build(BuildContext context) {
@@ -28,165 +25,113 @@ 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(
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,
child: RepaintBoundary(
child: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/background.jpeg"),
fit: BoxFit.cover,
),
// child: Container(
// decoration: 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: 100,
width: 100,
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: 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,
@@ -209,25 +154,25 @@ class _HomeScreenState extends State<HomeScreen> {
],
),
),
// 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,
// ),
// ],
// ),
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(
@@ -236,13 +181,12 @@ class _HomeScreenState extends State<HomeScreen> {
// color: Color(0xFF000000),
// ),
// ),
// ),
// SizedBox(height: 10),
// setHomeImage("images/luffy.png", 300),
// // Image.asset("images/luffy.png", width: 300),
// ],
// ),
// ),
),
SizedBox(height: 10),
setHomeImage("assets/images/cepott.png", 300),
],
),
),
// BG MEnu
Stack(
children: [
@@ -257,100 +201,43 @@ class _HomeScreenState extends State<HomeScreen> {
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,
: bottomPadding * 55,
left: (screenWidth - buttonScanSize) / 2,
child: Transform.translate(
offset: Offset(0, -30),
child: ScanButton(),
),
),
],
),
);
}
void onUnityCreated(controller) {
_unityWidgetController = controller;
}
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);
return Image.asset(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 +251,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,
),
),
),
],
),
// ),
);
}
}
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:freekake/components/collection_container.dart';
import 'package:freekake/screen/collection/character_view.dart';
class CollectionCaraterScreen extends StatefulWidget {
const CollectionCaraterScreen({super.key});
@@ -22,7 +23,13 @@ class _CollectionCaraterScreenState extends State<CollectionCaraterScreen> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Favorit", style: TextStyle(color: Colors.black)),
Text(
"Favorite",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
Divider(height: 20, color: Colors.transparent),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
@@ -31,46 +38,112 @@ 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: () => {},
),
],
),
),
Divider(height: 20, color: Colors.transparent),
Text("Semua", style: TextStyle(color: Colors.black)),
Divider(height: 20, color: Colors.transparent),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
// alignment: WrapAlignment.spaceAround,
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: "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.
""",
),
),
),
},
),
// 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: () => {},
// ),
],
),
),
Divider(height: 20, color: Colors.transparent),
Text(
"All",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
Divider(height: 20, color: Colors.transparent),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
// alignment: WrapAlignment.spaceAround,
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: "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.
""",
),
),
),
},
),
],
),
@@ -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"),
@@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:freekake/components/collection_container.dart';
import 'package:freekake/screen/collection/character_view.dart';
import 'package:freekake/screen/collection/skin_view.dart';
class CollectionSkinScreen extends StatefulWidget {
const CollectionSkinScreen({super.key});
@@ -11,7 +13,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 +25,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,
),
),
),
],
),
);
}
}
+10 -2
View File
@@ -41,7 +41,16 @@ class _KoleksiScreenState extends State<KoleksiScreen> {
final double bottomPadding = (9000 / screenWidth).clamp(0, 0.2);
return Scaffold(
extendBody: true,
body: 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)),
@@ -161,7 +170,6 @@ class _KoleksiScreenState extends State<KoleksiScreen> {
),
),
],
),
);
}
}
+105
View File
@@ -0,0 +1,105 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:webview_flutter/webview_flutter.dart';
class _ListDetailScreen extends StatefulWidget {
final String title;
final String? filepath;
const _ListDetailScreen({super.key, required this.title, this.filepath});
@override
State<_ListDetailScreen> createState() => _ListDetailScreenState();
}
class _ListDetailScreenState extends State<_ListDetailScreen> {
List<bool> checklist = [];
late WebViewController _controller;
late String? fPath = widget.filepath;
@override
void initState() {
super.initState();
_controller =
WebViewController()..setJavaScriptMode(JavaScriptMode.unrestricted);
checklist = List.generate(paragraphs.length, (index) => false);
if (fPath != null && fPath!.isNotEmpty) {
_loadContent(fPath!); // <- ini penting
}
}
final List<String> paragraphs = [
"Paragraf pertama tentang ${"widget.title"}.",
"Paragraf kedua dengan informasi tambahan.",
"Paragraf ketiga yang menjelaskan detail lebih lanjut.",
"Paragraf keempat yang melengkapi pembahasan.",
];
void _loadContent(String path) async {
try {
if (path.startsWith('http')) {
_controller.loadRequest(Uri.parse(path));
} else {
String fileHtml = await rootBundle.loadString(path);
_controller.loadHtmlString(fileHtml);
}
} catch (e) {
debugPrint("Error loading HTML: $e");
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(title: Text(widget.title)),
body:
(fPath != null && fPath!.isNotEmpty)
? WebViewWidget(controller: _controller)
: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(
'assets/images/default.png',
width: 100,
height: 100,
fit: BoxFit.cover,
),
const SizedBox(width: 16),
Expanded(
child: Text(
"Ini adalah detail dari ${widget.title}.",
style: const TextStyle(fontSize: 18),
),
),
],
),
const SizedBox(height: 20),
Expanded(
child: ListView.builder(
itemCount: paragraphs.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(paragraphs[index]),
trailing: Checkbox(
value: checklist[index],
onChanged: (bool? value) {
setState(() {
checklist[index] = value!;
});
},
),
);
},
),
),
],
),
),
);
}
}
+105 -73
View File
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'dart:convert';
class ListDetailScreen extends StatefulWidget {
final String title;
@@ -13,89 +14,120 @@ class ListDetailScreen extends StatefulWidget {
}
class _ListDetailScreenState extends State<ListDetailScreen> {
List<bool> checklist = [];
late WebViewController _controller;
late String? fPath = widget.filepath;
@override
void initState() {
super.initState();
checklist = List.generate(paragraphs.length, (index) => false);
}
final List<String> paragraphs = [
"Paragraf pertama tentang ${"widget.title"}.",
"Paragraf kedua dengan informasi tambahan.",
"Paragraf ketiga yang menjelaskan detail lebih lanjut.",
"Paragraf keempat yang melengkapi pembahasan.",
];
void _loadContent(String path) async {
if (path.startsWith('http')) {
// Load dari Internet
_controller.loadRequest(Uri.parse(path));
} else {
// Load dari assets lokal
String fileHtml = await rootBundle.loadString(path);
_controller.loadHtmlString(fileHtml);
}
}
late InAppWebViewController _webViewController;
@override
Widget build(BuildContext context) {
String _getMimeType(String path) {
if (path.endsWith('.png')) return 'image/png';
if (path.endsWith('.jpg') || path.endsWith('.jpeg')) return 'image/jpeg';
if (path.endsWith('.svg')) return 'image/svg+xml';
if (path.endsWith('.html')) return 'text/html';
if (path.endsWith('.css')) return 'text/css';
if (path.endsWith('.js')) return 'application/javascript';
return 'application/octet-stream';
}
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(title: Text(widget.title)),
body:
(fPath != null && fPath!.isNotEmpty)
? WebViewWidget(controller: _controller)
: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
// Gambar di kiri atas dengan teks di kanan
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(
'assets/images/default.png', // Ganti dengan gambar yang sesuai
width: 100,
height: 100,
fit: BoxFit.cover,
),
const SizedBox(width: 16),
Expanded(
child: Text(
"Ini adalah detail dari ${widget.title}.",
style: const TextStyle(fontSize: 18),
widget.filepath != null
? InAppWebView(
initialUrlRequest: URLRequest(url: WebUri("about:blank")),
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
javaScriptEnabled: true,
allowFileAccessFromFileURLs: true,
allowUniversalAccessFromFileURLs: true,
),
),
],
),
const SizedBox(height: 20),
onWebViewCreated: (controller) async {
_webViewController = controller;
// Paragraf dengan checklist
Expanded(
child: ListView.builder(
itemCount: paragraphs.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(paragraphs[index]),
trailing: Checkbox(
value: checklist[index],
onChanged: (bool? value) {
setState(() {
checklist[index] = value!;
});
},
),
final html = await rootBundle.loadString(widget.filepath!);
final base = widget.filepath!.substring(
0,
widget.filepath!.lastIndexOf('/') + 1,
);
await controller.loadData(
data: html,
baseUrl: WebUri(
"file:///android_asset/flutter_assets/$base",
), // agar path relatif seperti img1/* jalan
mimeType: 'text/html',
encoding: 'utf-8',
);
debugPrint("http://localhost/$base");
debugPrint("file:///android_asset/flutter_assets/$base");
},
),
),
],
),
),
onLoadStop: (controller, url) async {
debugPrint("🚀 onLoadStop called. URL: $url");
var result = await controller.evaluateJavascript(
source: """
(() => {
const board = document.getElementById('game-board');
const hasRender = typeof renderGrid === 'function';
return { hasRender, boardExists: board !== null };
})();
""",
);
debugPrint("📊 Evaluated JS result: $result");
final jsCheck = await controller.evaluateJavascript(
source: """
typeof renderGrid === 'function' &&
document.querySelector('#game-board') !== null
""",
);
debugPrint(
"Apakah renderGrid tersedia dan board muncul? => $jsCheck",
);
final imageLoaded = await controller.evaluateJavascript(
source: """
Array.from(document.images).every(img => img.complete)
""",
);
debugPrint("Semua gambar berhasil dimuat? => $imageLoaded");
},
// (Opsional) tangkap log dari JS
onConsoleMessage: (controller, consoleMessage) {
debugPrint("📢 JS Console: ${consoleMessage.message}");
},
shouldInterceptRequest: (controller, request) async {
final url = request.url;
if (url == null || url.host != 'localhost') return null;
try {
// Strip "http://localhost/" from the path
final path = url.path.replaceFirst('/', '');
final asset = await rootBundle.load(path);
final mimeType = _getMimeType(path);
return WebResourceResponse(
contentType: mimeType,
data: asset.buffer.asUint8List(),
statusCode: 200,
reasonPhrase: 'OK',
);
} catch (e) {
print("⚠️ Asset not found: ${url.path} -> $e");
return WebResourceResponse(
contentType: 'text/plain',
data: Uint8List.fromList(utf8.encode('404 Not Found')),
statusCode: 404,
reasonPhrase: 'Not Found',
);
}
},
)
: const Center(child: Text("Tidak ada file HTML")),
);
}
}
+197 -107
View File
@@ -3,16 +3,20 @@ 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/models/users.dart';
import 'package:freekake/screen/pustaka/list_detail_screen.dart';
import 'package:freekake/util/api_client.dart';
import 'package:list_detail_extension/list_detail_extension.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:dio/dio.dart';
class ListEducation extends StatefulWidget {
const ListEducation({super.key});
final String? selectedCategory;
const ListEducation({super.key, this.selectedCategory});
@override
State<ListEducation> createState() => _ListEducationState();
@@ -23,10 +27,15 @@ class _ListEducationState extends State<ListEducation> {
late final WebViewController _controller;
late String urlOrAssetPath;
bool _shouldShowWebView = false;
// load extension
String? selectedCategory;
final listDetailExtension = ListDetailExtension();
// Data fetch
final ApiClient _apiClient = ApiClient();
List<Users> _posts = [];
bool _isLoading = true;
String _error = '';
Future<void> _initExtension() async {
await listDetailExtension.load();
}
@@ -43,9 +52,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 +66,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)
@@ -64,41 +79,80 @@ class _ListEducationState extends State<ListEducation> {
""",
"file": "assets/html/index.html",
},
{
"title": "Memori Game",
"image": "assets/html/freekake.png",
"color": "#cef1da",
"category": "Gizi",
"type": "Materi",
"point": "100",
"coint": "20",
"body":
"""Game untuk melatih daya ingat siswa, sehingga bisa diharapkan bisa
menemukan cara sendiri untuk mengingat sesuatu
""",
"file": "assets/html/memory/memory.html",
},
{
"title": "Find Words",
"image": "assets/html/freekake.png",
"color": "#cef1da",
"category": "Kesehatan",
"type": "Materi",
"point": "100",
"coint": "20",
"body": """
No Descripptions
""",
"file": "assets/html/findwords/findwords.html",
},
];
List<Map<String, dynamic>> filteredItems = [];
@override
void initState() {
super.initState();
selectedCategory = widget.selectedCategory;
print(selectedCategory);
if (selectedCategory != null && selectedCategory!.isNotEmpty) {
filteredItems =
_listContent
.where((item) => item["category"] == selectedCategory)
.toList();
} else {
filteredItems = List.from(_listContent);
}
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
_shouldShowWebView = true;
_controller =
WebViewController()..setJavaScriptMode(JavaScriptMode.unrestricted);
urlOrAssetPath = "assets/makanan_bergizi/index.html";
urlOrAssetPath = "assets/html/index.html";
_loadContent(urlOrAssetPath);
_initExtension();
}
}
void _loadContent(String path) async {
if (_shouldShowWebView && _controller != null) {
if (_shouldShowWebView) {
if (path.startsWith('http')) {
// Load dari Internet
_controller.loadRequest(Uri.parse(path));
} else {
// Load dari assets lokal
// String fileHtml = await rootBundle.loadString(path);
String fileHtml = await DefaultAssetBundle.of(
context,
).loadString('assets/html/index.html');
_controller.loadHtmlString(fileHtml);
try {
await rootBundle.load(path);
await _controller.loadFlutterAsset(path);
} catch (e) {
print("Gagal memuat file HTML: $path\nError: $e");
}
_controller.loadFlutterAsset(path);
}
}
}
void filterSearch(String query) {
setState(() {
selectedCategory = null;
if (query.isEmpty) {
filteredItems = List.from(_listContent);
} else {
@@ -112,6 +166,7 @@ class _ListEducationState extends State<ListEducation> {
void filterByCategory(String category) {
setState(() {
selectedCategory = category;
filteredItems =
_listContent.where((item) {
return item["category"].toLowerCase() == category.toLowerCase();
@@ -119,10 +174,44 @@ class _ListEducationState extends State<ListEducation> {
});
}
Future<void> _fetchPosts() async {
try {
setState(() {
_isLoading = true;
_error = '';
});
// Melakukan permintaan GET ke endpoint
final Response response = await _apiClient.dio.get('auth/users');
if (response.statusCode == 200) {
// Konversi data JSON ke List
List<dynamic> postJson = response.data;
_posts = postJson.map((json) => Users.fromJson(json)).toList();
} else {
_error = 'Gagal memuat data: ${response.statusCode}';
}
} on DioException catch (e) {
if (e.response != null) {
_error =
'Server Error: ${e.response?.statusCode} - ${e.response?.statusMessage}';
} else {
_error = 'Network Error: ${e.message}';
}
print('DioError: $e');
} catch (e) {
_error = 'Terjadi kesalahan tidak terduga: $e';
print('General Error: $e');
} finally {
setState(() {
_isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
String _searchQuery = "";
final List<Map<String, dynamic>> _collections = [
final List<Map<String, dynamic>> collections = [
{
"label": "Kesehatan",
"image": "assets/icons/healthy.svg",
@@ -150,14 +239,15 @@ class _ListEducationState extends State<ListEducation> {
];
final List<Map<String, dynamic>> filteredCollections =
_collections
collections
.where(
(item) => item["label"].toLowerCase().contains(
_searchQuery.toLowerCase(),
// searchQuery.toLowerCase(),
searchController.text.toLowerCase(),
),
)
.toList();
if (_shouldShowWebView && _controller != null) {
if (_shouldShowWebView) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color.fromARGB(225, 79, 76, 182),
@@ -171,7 +261,28 @@ class _ListEducationState extends State<ListEducation> {
body: Column(
children: [
// Header Section
Container(
headerContainer(context, collections),
// Collection Items dengan Filter
SizedBox(height: 20),
Expanded(child: contentContainer(context)),
],
),
);
} else {
return const Center(
child: Padding(
padding: EdgeInsets.all(40),
child: Text("WebView tidak tersedia untuk platform ini."),
),
);
}
}
Container headerContainer(
BuildContext context,
List<Map<String, dynamic>> filteredCollections,
) {
return Container(
width: MediaQuery.of(context).size.width,
height: 150,
decoration: BoxDecoration(
@@ -184,10 +295,7 @@ class _ListEducationState extends State<ListEducation> {
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 6,
),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 6),
child: Container(
height: 47,
decoration: BoxDecoration(
@@ -206,12 +314,8 @@ class _ListEducationState extends State<ListEducation> {
Expanded(
child: TextField(
controller: searchController,
onChanged:
filterSearch, // Memanggil filter saat mengetik
style: const TextStyle(
color: Colors.black,
fontSize: 14,
),
onChanged: filterSearch, // Memanggil filter saat mengetik
style: const TextStyle(color: Colors.black, fontSize: 14),
decoration: const InputDecoration(
border: InputBorder.none,
hintText: 'Cari pelajaran...',
@@ -221,10 +325,7 @@ class _ListEducationState extends State<ListEducation> {
),
if (searchController.text.isNotEmpty)
IconButton(
icon: const Icon(
Icons.close,
color: Colors.black54,
),
icon: const Icon(Icons.close, color: Colors.black54),
onPressed: () {
searchController.clear();
filterSearch('');
@@ -239,6 +340,7 @@ class _ListEducationState extends State<ListEducation> {
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children:
filteredCollections.map((item) {
final isSelected = selectedCategory == item["category"];
return CollectionContainer(
label: item["label"]!,
imageSvg: item["image"]!,
@@ -246,11 +348,16 @@ class _ListEducationState extends State<ListEducation> {
iconh: 20,
width: 70,
height: 70,
textColor: Colors.black,
textColor:
isSelected
? const Color.fromARGB(255, 230, 48, 160)
: Colors.black,
lblSize: 10,
colorContiner: ColorHelper.hexToColor(
item["color"],
),
// colorContiner: ColorHelper.hexToColor(item["color"]),
colorContiner:
isSelected
? const Color.fromARGB(255, 84, 3, 224)
: ColorHelper.hexToColor(item["color"]),
onTapAc:
() => {
searchController.clear(),
@@ -261,11 +368,11 @@ class _ListEducationState extends State<ListEducation> {
),
],
),
),
// Collection Items dengan Filter
SizedBox(height: 20),
Expanded(
child: Container(
);
}
Container contentContainer(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: const Color.fromARGB(223, 200, 200, 206),
borderRadius: BorderRadius.only(
@@ -273,7 +380,12 @@ class _ListEducationState extends State<ListEducation> {
topRight: Radius.circular(25),
),
),
child: Column(
child: _listCard(context),
);
}
Column _listCard(BuildContext context) {
return Column(
children: [
SizedBox(height: 5),
Padding(
@@ -296,30 +408,18 @@ class _ListEducationState extends State<ListEducation> {
? const Center(
child: Text(
"Tidak ada hasil",
style: TextStyle(
fontSize: 16,
color: Colors.black54,
),
style: TextStyle(fontSize: 16, color: Colors.black54),
),
)
: Padding(
padding: EdgeInsets.only(
top: 10.0,
left: 10,
right: 10,
),
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,
),
color: const Color.fromARGB(255, 222, 181, 133),
),
child: Padding(
padding: EdgeInsets.only(
@@ -327,69 +427,59 @@ class _ListEducationState extends State<ListEducation> {
right: 20,
left: 20,
),
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1, // 2 Kolom
child: _generateList(),
),
),
),
),
),
],
);
}
GridView _generateList() {
return GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
childAspectRatio: 2.5,
),
itemCount: _listContent.length,
itemCount: filteredItems.length,
itemBuilder: (context, index) {
return GestureDetector(
child: CardList(
title:
_listContent[index]["title"] ??
"",
body:
_listContent[index]["body"] ??
"",
title: filteredItems[index]["title"] ?? "",
body: filteredItems[index]["body"] ?? "",
gambar: filteredItems[index]['image'],
point: filteredItems[index]['point'],
coint: filteredItems[index]['coint'],
tipe: filteredItems[index]['type'],
),
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"]}",
}),
// (context) => listDetailExtension.buildPage({
// 'title': filteredItems[index]['category'],
// 'imagePath': filteredItems[index]['image'],
// 'filepath': filteredItems[index]['file'],
// 'paragraphs':
// """
// """
// "<h1> ${filteredItems[index]["title"]} </h1> <p> ${filteredItems[index]["body"]}",
// }),
(context) => ListDetailScreen(
title: filteredItems[index]['category'],
// 'imagePath': filteredItems[index]['image'],
filepath: filteredItems[index]['file'],
),
),
);
},
);
},
),
),
),
),
),
),
],
),
),
),
],
),
);
} else {
return const Center(
child: Padding(
padding: EdgeInsets.all(40),
child: Text("WebView tidak tersedia untuk platform ini."),
),
);
}
}
}
@@ -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 {
+40 -105
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;
});
@@ -69,106 +74,21 @@ class _ListDetailScreenState extends State<ListDetailScreen> {
),
),
),
// Padding(
// padding: const EdgeInsets.all(20.0),
// child: Row(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Image.asset(
// widget.imagePath,
// width: 100,
// height: 100,
// fit: BoxFit.cover,
// ),
// const SizedBox(width: 16),
// Expanded(
// child: Center(
// child: Text(
// widget.title,
// style: const TextStyle(
// fontSize: 22,
// fontWeight: FontWeight.bold,
// color: Colors.black,
// ),
// ),
// ),
// ),
// ],
// ),
// ),
const SizedBox(height: 0),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.only(left: 18.0, right: 18, bottom: 18),
child:
// ListView.builder(
// itemCount: widget.paragraphs.length,
// itemBuilder: (context, index) {
// return
// Padding(
// padding: const EdgeInsets.only(bottom: 16.0),
// child: Row(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Expanded(
// child:
// // Text(
// // widget.paragraphs[index] +
// // style: TextStyle(color: Colors.black),
// Html(
// // style: {
// // "body": Style(
// // color: const Color.fromRGBO(0, 0, 0, 1),
// // ),
// // },
// data:
// widget.paragraphs +
// """
// <html>
// <!-- Text between angle brackets is an HTML tag and is not displayed.
// Most tags, such as the HTML and /HTML tags that surround the contents of
// a page, come in pairs; some tags, like HR, for a horizontal rule, stand
// alone. Comments, such as the text you're reading, are not displayed when
// the Web page is shown. The information between the HEAD and /HEAD tags is
// not displayed. The information between the BODY and /BODY tags is displayed.-->
// <head>
// <title>Enter a title, displayed at the top of the window.</title>
// </head>
// <!-- The information between the BODY and /BODY tags is displayed.-->
// <body>
// <h1>Enter the main heading, usually the same as the title.</h1>
// <p>Be <b>bold</b> in stating your key points. Put them in a list: </p>
// <ul>
// <li>The first item in your list</li>
// <li>The second item; <i>italicize</i> key words</li>
// </ul>
// <p>Improve your image by including an image. </p>
// <p><img src="http://www.mygifs.com/CoverImage.gif" alt="A Great HTML Resource"></p>
// <p>Add a link to your favorite <a href="https://www.dummies.com/">Web site</a>.
// Break up your page with a horizontal rule or two. </p>
// <hr>
// <p>Finally, link to <a href="page2.html">another page</a> in your own Web site.</p>
// <!-- And add a copyright notice.-->
// <p>© Wiley Publishing, 2011</p>
// </body>
// </html>
// """,
child: htmlBody(),
),
),
],
),
// ),
// ),
// // ),
// // Checkbox(
// // value: _readStatus[index],
// // onChanged: (bool? value) {
// // _toggleReadStatus(index);
// // },
// // ),
// ],
// ),
// );
// },
// ),
Html(
);
}
Html htmlBody() {
return Html(
style: {"body": Style(color: const Color.fromRGBO(0, 0, 0, 1))},
// data: widget.paragraphs,
data: _htmlContent,
@@ -209,13 +129,28 @@ class _ListDetailScreenState extends State<ListDetailScreen> {
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(),
);
},
),
],
),
// ),
);
}
}
+7 -7
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});
@@ -108,7 +108,7 @@ class _PustakaScreenState extends State<PustakaScreen> {
children: [
CollectionContainer(
label: "Luffy",
imagesrc: 'assets/images/luffy.png',
imagesrc: 'images/luffy.png',
width: 100,
height: 100,
onTapAc:
+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});
+7 -7
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});
@@ -108,7 +108,7 @@ class _PustakaScreenState extends State<PustakaScreen> {
children: [
CollectionContainer(
label: "Luffy",
imagesrc: 'assets/images/luffy.png',
imagesrc: 'images/luffy.png',
width: 100,
height: 100,
onTapAc:
+114 -69
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,52 @@ 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",
},
];
final List<Map<String, dynamic>> _genres = [
{
"label": "Game",
"type": "game",
"image": "assets/icons/healthy.svg",
"color": "#cdd0ee",
},
{
"label": "Novel",
"image": "assets/icons/Nutrition.svg",
"color": "#e8e29a",
},
{
"label": "TTS",
"type": "puzzle",
"image": "assets/icons/Education.svg",
"color": "#efd8c6",
},
{
"label": "Pazzle",
"type": "puzzle",
"image": "assets/icons/Safety.svg",
"color": "#cef1da",
},
];
void _onMenuTapped(int index) {
@@ -52,18 +86,44 @@ class _PustakaScreenState extends State<PustakaScreen> {
final screenWidth = MediaQuery.of(context).size.width;
final buttonScanSize = screenWidth * 0.20;
final double bottomPadding = (9000 / screenWidth).clamp(0, 0.2);
List<Map<String, dynamic>> filteredCollections =
_collections
List<Map<String, dynamic>> filterCollections(
List<Map<String, dynamic>> collections,
String query,
) {
return collections
.where(
(item) => item["label"]!.toLowerCase().contains(
_searchQuery.toLowerCase(),
),
(item) =>
item["label"]!.toLowerCase().contains(query.toLowerCase()),
)
.toList();
}
List<Map<String, dynamic>> filteredCollections = filterCollections(
_collections,
_searchQuery,
);
return Scaffold(
backgroundColor: const Color.fromARGB(255, 255, 255, 255),
body: Stack(
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(
@@ -77,12 +137,7 @@ class _PustakaScreenState extends State<PustakaScreen> {
// Header Section
Container(
padding: const EdgeInsets.all(10),
color: const Color.fromARGB(
250,
255,
255,
255,
).withOpacity(0.0),
color: const Color.fromARGB(250, 255, 255, 255).withOpacity(0.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@@ -101,22 +156,9 @@ class _PustakaScreenState extends State<PustakaScreen> {
],
),
),
// // 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,
),
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 10),
child: Container(
height: 47,
decoration: BoxDecoration(
@@ -142,7 +184,7 @@ class _PustakaScreenState extends State<PustakaScreen> {
),
decoration: const InputDecoration(
border: InputBorder.none,
hintText: 'Cari konten...',
hintText: 'Search...',
hintStyle: TextStyle(color: Colors.grey),
),
),
@@ -152,7 +194,7 @@ class _PustakaScreenState extends State<PustakaScreen> {
),
),
// Collection Items
Container(
SizedBox(
height: 440,
child: Padding(
padding: const EdgeInsets.symmetric(
@@ -160,8 +202,7 @@ class _PustakaScreenState extends State<PustakaScreen> {
vertical: 60,
),
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithMaxCrossAxisExtent(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 140,
mainAxisSpacing: 20,
crossAxisSpacing: 20,
@@ -172,7 +213,6 @@ class _PustakaScreenState extends State<PustakaScreen> {
final item = filteredCollections[index];
return CollectionContainer(
label: item["label"]!,
lblSize: 11,
imageSvg: item["image"]!,
width: 30,
height: 80,
@@ -183,7 +223,10 @@ class _PustakaScreenState extends State<PustakaScreen> {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const ListEducation(),
builder:
(_) => ListEducation(
selectedCategory: item["label"],
),
),
);
},
@@ -192,21 +235,21 @@ class _PustakaScreenState extends State<PustakaScreen> {
),
),
),
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),
],
),
),
),
// 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),
@@ -216,10 +259,12 @@ class _PustakaScreenState extends State<PustakaScreen> {
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: List.generate(
10,
(index) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
children:
_genres.map((genre) {
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
@@ -252,10 +297,9 @@ class _PustakaScreenState extends State<PustakaScreen> {
color: Colors.blueAccent,
),
Text(
'Topik ' + (index + 1).toString(),
genre['label'],
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 11,
color: Color.fromARGB(255, 0, 0, 0),
),
),
@@ -264,14 +308,12 @@ class _PustakaScreenState extends State<PustakaScreen> {
),
),
),
);
}).toList(),
),
),
),
),
),
),
// Bottom Navigation
// MainMenu(),
],
),
// BG MEnu
@@ -286,6 +328,8 @@ class _PustakaScreenState extends State<PustakaScreen> {
Positioned(bottom: 0, left: 0, right: 0, child: MainMenu()),
],
),
Stack(
children: [
Positioned(
bottom: bottomPadding * 98,
left: (screenWidth - buttonScanSize) / 2,
@@ -296,6 +340,7 @@ class _PustakaScreenState extends State<PustakaScreen> {
),
],
),
],
);
}
}
+13 -20
View File
@@ -7,9 +7,12 @@ 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 AkunSaya extends StatefulWidget {
const AkunSaya({super.key});
@override
_AkunSayaState createState() => _AkunSayaState();
}
@@ -72,7 +75,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 +125,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 +143,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),
),
],
),
),
@@ -1,34 +1,58 @@
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: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 AkunSaya extends StatefulWidget {
class DataSaya extends StatefulWidget {
const DataSaya({super.key});
@override
_AkunSayaState createState() => _AkunSayaState();
_DataSayaState createState() => _DataSayaState();
}
class _AkunSayaState extends State<AkunSaya> {
class _DataSayaState extends State<DataSaya> {
dynamic _profileImage;
dynamic _headerImage;
//final ImagePicker _picker = ImagePicker();
ImageProvider? imageProvider;
final ImagePicker _picker = ImagePicker();
Future<void> _pickImage(bool isProfile) async {
final imagePicker = ImagePickerHelper();
dynamic pic = await imagePicker.pickImage();
if (pic != null) {
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 = pic;
_profileImage = imageFile;
} else {
_headerImage = pic;
_headerImage = imageFile;
}
imageProvider =
kIsWeb ? MemoryImage(_profileImage) : FileImage(_profileImage);
});
} else {
imageProvider = AssetImage("assets/images/luffy.png");
}
}
}
@@ -36,23 +60,22 @@ class _AkunSayaState extends State<AkunSaya> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Saya"),
title: Text("Data Pribadi"),
backgroundColor: Color.fromARGB(225, 79, 76, 182),
),
resizeToAvoidBottomInset: false,
backgroundColor: const Color.fromARGB(255, 255, 255, 255),
body: Stack(
children: [
Positioned(
bottom: 0,
left: 0,
right: 0,
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.only(top: 20.0, left: 20, right: 20),
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, 205, 202, 189),
color: const Color.fromARGB(255, 212, 211, 208),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
@@ -63,7 +86,6 @@ class _AkunSayaState extends State<AkunSaya> {
),
Column(
children: [
// Header image (bisa diubah)
SizedBox(
height: 80,
child: Container(
@@ -76,7 +98,7 @@ class _AkunSayaState extends State<AkunSaya> {
),
),
),
SizedBox(height: 40),
SizedBox(height: 70),
Expanded(
child: Padding(
padding: EdgeInsets.all(20),
@@ -84,48 +106,75 @@ class _AkunSayaState extends State<AkunSaya> {
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 40),
children: [
TextFormField(
initialValue: "Cepot",
initialValue: "Hasan",
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Nama',
labelText: 'Nama Siswa',
labelStyle: TextStyle(color: Colors.black),
),
style: TextStyle(color: Colors.black),
),
TextFormField(
initialValue: "cpt09",
initialValue: "Jawa Barat",
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Nama Pengguna',
labelText: 'Provinsi',
labelStyle: TextStyle(color: Colors.black),
),
style: TextStyle(color: Colors.black),
),
TextFormField(
initialValue: "Jl. Sentosa jaya",
initialValue: "Bandung",
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Alamat',
labelText: 'Kabupaten',
labelStyle: TextStyle(color: Colors.black),
),
style: TextStyle(color: Colors.black),
),
TextFormField(
initialValue: "******",
initialValue: "SDN 1 Bandung Barat",
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Kata Sandi',
labelText: 'Sekolah',
labelStyle: TextStyle(color: Colors.black),
),
style: TextStyle(color: Colors.black),
),
TextFormField(
initialValue: "+62923",
initialValue: "Laki-laki",
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'No. HP',
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),
@@ -156,7 +205,7 @@ class _AkunSayaState extends State<AkunSaya> {
: null,
child:
_profileImage == null
? Icon(Icons.person, size: 50, color: Colors.white)
? Image.asset("assets/images/luffy.png")
: null,
),
CircleAvatar(
@@ -171,14 +220,14 @@ class _AkunSayaState extends State<AkunSaya> {
Positioned(
top: 130,
left: MediaQuery.of(context).size.width / 2 - 50,
left: MediaQuery.of(context).size.width / 2 - 30,
child: GestureDetector(
onTap: () => _pickImage(true),
child: Stack(
alignment: Alignment.bottomRight,
children: [
Text(
"Nama Akun",
"Luffy kun",
style: TextStyle(
color: Colors.deepPurple,
fontSize: 18,
+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();
}
+20 -3
View File
@@ -1,18 +1,23 @@
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:image_picker_web/image_picker_web.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
class ProfileScreen extends StatefulWidget {
const ProfileScreen({super.key});
@override
_ProfileScreenState createState() => _ProfileScreenState();
}
@@ -212,12 +217,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,
+36
View File
@@ -0,0 +1,36 @@
import 'package:dio/dio.dart';
class ApiClient {
final Dio _dio = Dio();
final String _baseUrl = 'https://api.freekake.com/';
ApiClient() {
_dio.options.baseUrl = _baseUrl;
_dio.options.connectTimeout = const Duration(seconds: 5);
_dio.options.receiveTimeout = const Duration(seconds: 3);
_dio.interceptors.add(
InterceptorsWrapper(
onRequest: (options, handler) {
print('REQUEST[${options.method}] => PATH: ${options.path}');
// options.headers['Authorization'] = 'Bearer your_token_here';
return handler.next(options);
},
onResponse: (response, handler) {
print(
'RESPONSE[${response.statusCode}] => PATH: ${response.requestOptions.path}',
);
return handler.next(response);
},
onError: (DioException e, handler) {
print(
'ERROR[${e.response?.statusCode}] => PATH: ${e.requestOptions.path}',
);
return handler.next(e);
},
),
);
}
Dio get dio => _dio;
}
+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;
}
}
@@ -7,6 +7,7 @@ import Foundation
import desktop_webview_window
import file_selector_macos
import flutter_inappwebview_macos
import shared_preferences_foundation
import url_launcher_macos
import webview_flutter_wkwebview
@@ -14,6 +15,7 @@ import webview_flutter_wkwebview
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DesktopWebviewWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewWindowPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin"))
+80 -9
View File
@@ -129,6 +129,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.2.3"
dio:
dependency: "direct main"
description:
name: dio
sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9"
url: "https://pub.dev"
source: hosted
version: "5.8.0+1"
dio_web_adapter:
dependency: transitive
description:
name: dio_web_adapter
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
dropdown_button2:
dependency: "direct main"
description:
@@ -206,6 +222,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.0-beta.2"
flutter_inappwebview:
dependency: "direct main"
description:
name: flutter_inappwebview
sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5"
url: "https://pub.dev"
source: hosted
version: "6.1.5"
flutter_inappwebview_android:
dependency: transitive
description:
name: flutter_inappwebview_android
sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba"
url: "https://pub.dev"
source: hosted
version: "1.1.3"
flutter_inappwebview_internal_annotations:
dependency: transitive
description:
name: flutter_inappwebview_internal_annotations
sha256: "787171d43f8af67864740b6f04166c13190aa74a1468a1f1f1e9ee5b90c359cd"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
flutter_inappwebview_ios:
dependency: transitive
description:
name: flutter_inappwebview_ios
sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d"
url: "https://pub.dev"
source: hosted
version: "1.1.2"
flutter_inappwebview_macos:
dependency: transitive
description:
name: flutter_inappwebview_macos
sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1
url: "https://pub.dev"
source: hosted
version: "1.1.2"
flutter_inappwebview_platform_interface:
dependency: transitive
description:
name: flutter_inappwebview_platform_interface
sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500
url: "https://pub.dev"
source: hosted
version: "1.3.0+1"
flutter_inappwebview_web:
dependency: transitive
description:
name: flutter_inappwebview_web
sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598"
url: "https://pub.dev"
source: hosted
version: "1.1.2"
flutter_inappwebview_windows:
dependency: transitive
description:
name: flutter_inappwebview_windows
sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055"
url: "https://pub.dev"
source: hosted
version: "0.6.0"
flutter_lints:
dependency: "direct dev"
description:
@@ -235,15 +315,6 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_unity_widget:
dependency: "direct main"
description:
path: "."
ref: "flutter_3.24_android_hotfix"
resolved-ref: d2f6dc853d5b5054668ea9d79bb138d2d072696c
url: "https://github.com/juicycleff/flutter-unity-view-widget.git"
source: git
version: "2022.2.1"
flutter_web_plugins:
dependency: transitive
description: flutter
+51 -6
View File
@@ -54,16 +54,13 @@ dependencies:
url: https://github.com/fhauze/list_detail_screen.git
ref: main
desktop_webview_window: ^0.2.3
flutter_inappwebview: ^6.0.0
dio: ^5.0.0
# arcore_flutter_plugin: ^0.2.0-alpha
# flutter_unity_widget: ^2022.2.1
# arcore_flutter_plugin:
# git:
# url: https://github.com/giandifra/arcore_flutter_plugin.git
# flutter_unity_widget: ^2022.2.1
flutter_unity_widget:
git:
url: https://github.com/juicycleff/flutter-unity-view-widget.git
ref: flutter_3.24_android_hotfix
dev_dependencies:
flutter_test:
@@ -90,9 +87,57 @@ flutter:
# To add assets to your application, add an assets section, like this:
assets:
- assets/images/
- assets/images/Artboard 15@4x.png
- assets/images/Artboard 16@4x.png
- assets/images/background.jpeg
- assets/images/cepott.png
- assets/images/ft_cepot.png
- assets/images/ft_luffy.png
- assets/images/klipartz.png
- assets/images/luffy-kimono.png
- assets/images/luffy-king.png
- assets/images/luffy-u.png
- assets/images/luffy.gif
- assets/images/luffy.png
# SVG files
- assets/icons/
- assets/icons/E-furrybuddy.svg
- assets/icons/E-furrybuddy1.svg
- assets/icons/Education.svg
- assets/icons/furrybuddy.svg
- assets/icons/healthy.svg
- assets/icons/Koleksi.svg
- assets/icons/Nutrition.svg
- assets/icons/Pustaka.svg
- assets/icons/Safety.svg
- assets/icons/Saya.svg
- assets/icons/Scan.svg
#HTML File
- assets/html/
- assets/html/index.html
- assets/html/furikake/index.html
- assets/html/furikake/
# HTML Images
- assets/html/isipiringku.png
- assets/html/karbohidrat.png
- assets/html/lemak.png
- assets/html/protein.png
- assets/html/seratair.png
- assets/html/vitaminmineral.png
#drag and drop
- assets/html/findwords/
- assets/html/findwords/test.html
- assets/html/findwords/findwords.html
- assets/html/freekake.png
- assets/html/memory/
- assets/html/memory/memory.html
#assets puzzle memory
- assets/html/memory/freekake.png
# 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 {
@@ -8,6 +8,7 @@
#include <desktop_webview_window/desktop_webview_window_plugin.h>
#include <file_selector_windows/file_selector_windows.h>
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
@@ -16,6 +17,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("DesktopWebviewWindowPlugin"));
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(
+1
View File
@@ -5,6 +5,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
desktop_webview_window
file_selector_windows
flutter_inappwebview_windows
permission_handler_windows
url_launcher_windows
)