update profile and html content

This commit is contained in:
='fauz
2025-05-05 18:11:34 +07:00
parent 5bfb381736
commit 4dad153d28
29 changed files with 1121 additions and 498 deletions
+2
View File
@@ -9,10 +9,12 @@ void main() async {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown, // bisa dihapus jika hanya ingin satu arah
]);
runApp(const MyApp());
}
+65
View File
@@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_html/flutter_html.dart';
class CharacterView extends StatefulWidget {
final String title;
final String imagePath;
final String content;
const CharacterView({
Key? key,
required this.title,
required this.imagePath,
required this.content,
}) : super(key: key);
@override
_CharacterViewState createState() => _CharacterViewState();
}
class _CharacterViewState extends State<CharacterView> {
String _content = '';
@override
void initState() {
super.initState();
_content = widget.content;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text(widget.title),
backgroundColor: Color.fromARGB(225, 79, 76, 182),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 10,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(40),
bottomRight: Radius.circular(40),
),
color: Color.fromARGB(225, 79, 76, 182),
),
),
),
const SizedBox(height: 0),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.only(left: 18.0, right: 18, bottom: 18),
child: Text(_content),
),
),
],
),
// ),
);
}
}
+65 -46
View File
@@ -1,9 +1,12 @@
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});
const ListDetailScreen({super.key, required this.title, this.filepath});
@override
State<ListDetailScreen> createState() => _ListDetailScreenState();
@@ -11,6 +14,8 @@ class ListDetailScreen extends StatefulWidget {
class _ListDetailScreenState extends State<ListDetailScreen> {
List<bool> checklist = [];
late WebViewController _controller;
late String? fPath = widget.filepath;
@override
void initState() {
@@ -25,58 +30,72 @@ class _ListDetailScreenState extends State<ListDetailScreen> {
"Paragraf keempat yang melengkapi pembahasan.",
];
void _loadContent(String path) async {
if (path.startsWith('http')) {
// Load dari Internet
_controller.loadRequest(Uri.parse(path));
} else {
// Load dari assets lokal
String fileHtml = await rootBundle.loadString(path);
_controller.loadHtmlString(fileHtml);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(title: Text(widget.title)),
body: 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),
),
),
],
),
const SizedBox(height: 20),
// 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!;
});
},
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),
),
),
],
),
);
},
const SizedBox(height: 20),
// 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!;
});
},
),
);
},
),
),
],
),
),
),
],
),
),
);
}
}
+310 -430
View File
@@ -1,11 +1,18 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:furibase/components/buildcard_info.dart';
import 'package:flutter/services.dart';
import 'package:furibase/components/card_list.dart';
import 'package:furibase/components/collection_container%20copy.dart';
import 'package:furibase/components/main_menu.dart';
import 'package:furibase/components/topbar_container.dart';
// import 'package:furibase/components/main_menu.dart';
// import 'package:furibase/components/topbar_container.dart';
import 'package:furibase/helpers/color_helper.dart';
import 'package:furibase/screen/pustaka/pustaka_detail_screen.dart';
import 'package:list_detail_extension/list_detail_extension.dart';
// import 'package:furibase/screen/pustaka/pustaka_detail_screen.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
class ListEducation extends StatefulWidget {
const ListEducation({super.key});
@@ -16,6 +23,17 @@ class ListEducation extends StatefulWidget {
class _ListEducationState extends State<ListEducation> {
TextEditingController searchController = TextEditingController();
late final WebViewController _controller;
late String urlOrAssetPath;
bool _shouldShowWebView = false;
// load extension
final listDetailExtension = ListDetailExtension();
Future<void> _initExtension() async {
await listDetailExtension.load();
}
List<String> allItems = [
"Matematika",
"Fisika",
@@ -24,20 +42,83 @@ class _ListEducationState extends State<ListEducation> {
"Sejarah",
"Geografi",
];
List<String> filteredItems = [];
final List<Map<String, dynamic>> _listContent = [
{
"title": "Taburan Lezat untuk Nasi!",
"image": "assets/icons/healthy.svg",
"color": "#cdd0ee",
"category": "Kesehatan",
"body":
"""Tahukah kamu bahwa di Jepang ada bumbu tabur yang bisa membuat nasi
menjadi lebih enak?
""",
"file": "assets/html/furikake/index.html",
},
{
"title": "Makan Sehat dengan Gizi Seimbang",
"image": "assets/icons/Safety.svg",
"color": "#cef1da",
"category": "Keselamatan",
"body":
"""upaya makan kita sehat, ada aturan "Isi Piringku" yang bisa kita ikuti:
🍽️ Separuh piring: Sayur dan buah 🍽️ Seperempat piring: Karbohidrat (seperti nasi atau roti)
🍽️ Seperempat piring: Protein (seperti ayam atau tempe) 🍽️ Sedikit lemak sehat
""",
"file": "assets/html/index.html",
},
];
List<Map<String, dynamic>> filteredItems = [];
@override
void initState() {
super.initState();
filteredItems = List.from(allItems);
filteredItems = List.from(_listContent);
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
_shouldShowWebView = true;
_controller =
WebViewController()..setJavaScriptMode(JavaScriptMode.unrestricted);
urlOrAssetPath = "assets/makanan_bergizi/index.html";
_loadContent(urlOrAssetPath);
_initExtension();
}
}
void _loadContent(String path) async {
if (_shouldShowWebView && _controller != null) {
if (path.startsWith('http')) {
// Load dari Internet
_controller.loadRequest(Uri.parse(path));
} else {
// Load dari assets lokal
// String fileHtml = await rootBundle.loadString(path);
String fileHtml = await DefaultAssetBundle.of(
context,
).loadString('assets/html/index.html');
_controller.loadHtmlString(fileHtml);
}
}
}
void filterSearch(String query) {
setState(() {
if (query.isEmpty) {
filteredItems = List.from(_listContent);
} else {
filteredItems =
_listContent.where((item) {
return item["title"].toLowerCase().contains(query.toLowerCase());
}).toList();
}
});
}
void filterByCategory(String category) {
setState(() {
filteredItems =
allItems
.where((item) => item.toLowerCase().contains(query.toLowerCase()))
.toList();
_listContent.where((item) {
return item["category"].toLowerCase() == category.toLowerCase();
}).toList();
});
}
@@ -71,32 +152,7 @@ class _ListEducationState extends State<ListEducation> {
},
];
final List<Map<String, dynamic>> _listContent = [
{
"title": "Taburan Lezat untuk Nasi!",
"image": "assets/icons/healthy.svg",
"color": "#cdd0ee",
"category": "kesehatan",
"body":
"""Tahukah kamu bahwa di Jepang ada bumbu tabur yang bisa membuat nasi
menjadi lebih enak? Namanya Furikake! Kata "furikake" berasal dari bahasa Jepang, yaitu "furi"
yang berarti menaburkan dan "kake" yang berarti menuangkan. Jadi,
furikake adalah bumbu yang ditaburkan di atas nasi supaya lebih lezat!
""",
},
{
"title": "Makan Sehat dengan Gizi Seimbang",
"image": "assets/icons/Safety.svg",
"color": "#cef1da",
"category": "keselamatan",
"body":
"""upaya makan kita sehat, ada aturan "Isi Piringku" yang bisa kita ikuti:
🍽️ Separuh piring: Sayur dan buah 🍽️ Seperempat piring: Karbohidrat (seperti nasi atau roti)
🍽️ Seperempat piring: Protein (seperti ayam atau tempe) 🍽️ Sedikit lemak sehat
""",
},
];
List<Map<String, dynamic>> filteredCollections =
final List<Map<String, dynamic>> filteredCollections =
_collections
.where(
(item) => item["label"].toLowerCase().contains(
@@ -104,415 +160,239 @@ class _ListEducationState extends State<ListEducation> {
),
)
.toList();
return Scaffold(
appBar: AppBar(
backgroundColor: Color.fromARGB(225, 79, 76, 182),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.of(context).pop(false),
),
actions: <Widget>[],
),
backgroundColor: const Color.fromARGB(255, 255, 255, 255),
body: Column(
children: [
// Header Section
Container(
width: MediaQuery.of(context).size.width,
height: 150,
decoration: BoxDecoration(
color: const Color.fromARGB(225, 79, 76, 182),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(25),
bottomRight: Radius.circular(25),
),
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 6,
),
child: Container(
height: 47,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.grey.shade200,
),
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Row(
children: [
const Icon(
Icons.search,
color: Color.fromARGB(137, 180, 172, 172),
size: 18,
),
const SizedBox(width: 8),
Expanded(
child: TextField(
controller: searchController,
onChanged:
filterSearch, // Memanggil filter saat mengetik
style: const TextStyle(
color: Colors.black,
fontSize: 14,
),
decoration: const InputDecoration(
border: InputBorder.none,
hintText: 'Cari pelajaran...',
hintStyle: TextStyle(color: Colors.black54),
),
),
),
if (searchController.text.isNotEmpty)
IconButton(
icon: const Icon(
Icons.close,
color: Colors.black54,
),
onPressed: () {
searchController.clear();
filterSearch('');
},
),
],
),
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children:
filteredCollections.map((item) {
return CollectionContainer(
label: item["label"]!,
imageSvg: item["image"]!,
iconw: 20,
iconh: 20,
width: 70,
height: 70,
textColor: Colors.black,
lblSize: 10,
colorContiner: ColorHelper.hexToColor(item["color"]),
onTapAc:
() => {
searchController.clear(),
filterSearch(item["category"]),
},
);
}).toList(),
),
],
),
if (_shouldShowWebView && _controller != null) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color.fromARGB(225, 79, 76, 182),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.of(context).pop(false),
),
// Search Bar
// Padding(
// padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
// child: Container(
// height: 47,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(20),
// color: Colors.grey.shade200,
// ),
// padding: const EdgeInsets.symmetric(horizontal: 10),
// child: Row(
// children: [
// const Icon(
// Icons.search,
// color: Color.fromARGB(137, 180, 172, 172),
// size: 18,
// ),
// const SizedBox(width: 8),
// Expanded(
// child: TextField(
// controller: searchController,
// onChanged: filterSearch, // Memanggil filter saat mengetik
// style: const TextStyle(color: Colors.black, fontSize: 14),
// decoration: const InputDecoration(
// border: InputBorder.none,
// hintText: 'Cari pelajaran...',
// hintStyle: TextStyle(color: Colors.black54),
// ),
// ),
// ),
// if (searchController.text.isNotEmpty)
// IconButton(
// icon: const Icon(Icons.close, color: Colors.black54),
// onPressed: () {
// searchController.clear();
// filterSearch('');
// },
// ),
// ],
// ),
// ),
// ),
// Collection Items dengan Filter
SizedBox(height: 20),
Expanded(
child: Container(
actions: <Widget>[],
),
backgroundColor: const Color.fromARGB(255, 255, 255, 255),
body: Column(
children: [
// Header Section
Container(
width: MediaQuery.of(context).size.width,
height: 150,
decoration: BoxDecoration(
color: const Color.fromARGB(224, 145, 145, 155),
color: const Color.fromARGB(225, 79, 76, 182),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(25),
topRight: Radius.circular(25),
bottomLeft: Radius.circular(25),
bottomRight: Radius.circular(25),
),
),
child: Column(
children: [
SizedBox(height: 5),
Padding(
padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width / 4,
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 6,
),
child: Divider(
color: const Color.fromARGB(255, 155, 147, 147),
height: 20,
child: Container(
height: 47,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.grey.shade200,
),
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Row(
children: [
const Icon(
Icons.search,
color: Color.fromARGB(137, 180, 172, 172),
size: 18,
),
const SizedBox(width: 8),
Expanded(
child: TextField(
controller: searchController,
onChanged:
filterSearch, // Memanggil filter saat mengetik
style: const TextStyle(
color: Colors.black,
fontSize: 14,
),
decoration: const InputDecoration(
border: InputBorder.none,
hintText: 'Cari pelajaran...',
hintStyle: TextStyle(color: Colors.black54),
),
),
),
if (searchController.text.isNotEmpty)
IconButton(
icon: const Icon(
Icons.close,
color: Colors.black54,
),
onPressed: () {
searchController.clear();
filterSearch('');
},
),
],
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
// vertical: 10,
),
child:
filteredItems.isEmpty
? const Center(
child: Text(
"Tidak ada hasil",
style: TextStyle(
fontSize: 16,
color: Colors.black54,
),
),
)
: Padding(
padding: EdgeInsets.only(
top: 20.0,
left: 20,
right: 20,
),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
color: const Color.fromARGB(
255,
222,
181,
133,
),
),
child: Padding(
padding: EdgeInsets.only(
top: 12,
right: 20,
left: 20,
),
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1, // 2 Kolom
crossAxisSpacing: 10,
mainAxisSpacing: 10,
childAspectRatio: 2.5,
),
itemCount: _listContent.length,
itemBuilder: (context, index) {
return GestureDetector(
child: CardList(
title:
_listContent[index]["title"] ??
"",
body:
_listContent[index]["body"] ??
"",
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder:
(
context,
) => ListDetailScreen(
title:
filteredItems[index],
imagePath:
filteredItems[index],
/* paragraphs: [
"""
<h1>Enter the main heading, usually the same as the title.</h1>
<p>Be <b>bold</b> in stating your key points. Put them in a list: </p>
<ul>
<li>The first item in your list</li>
<li>The second item; <i>italicize</i> key words</li>
</ul>
<p>Improve your image by including an image. </p>
<p><img src="https://via.placeholder.com/150" alt="A Great HTML Resource"></p>
<p>Add a link to your favorite <a href="https://www.dummies.com/">Web site</a>.</p>
<hr>
<p>Finally, link to <a href="page2.html">another page</a> in your own Web site.</p>
<p>© Wiley Publishing, 2011</p>
""",
], */
paragraphs: """
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Furikake: Taburan Lezat untuk Nasi!</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Roboto', sans-serif;
line-height: 1.5;
color: #333;
background-color: #fff;
padding: 15px;
max-width: 100%;
max-height:100%;
overflow-x: hidden;
}
h1 {
color: #d32f2f;
text-align: center;
font-size: 1.5rem;
margin: 10px 0 15px 0;
font-weight: 500;
}
h2 {
color: #f57c00;
font-size: 1.3rem;
margin: 20px 0 10px 0;
font-weight: 500;
padding-bottom: 3px;
border-bottom: 1px solid #eee;
}
p {
margin-bottom: 15px;
font-size: 0.95rem;
text-align: left;
}
.kanji {
font-size: 2.2rem;
text-align: center;
color: #333;
margin: 10px 0;
font-family: 'Noto Sans JP', sans-serif;
line-height: 1;
}
.main-image {
width: 100%;
margin: 15px 0;
text-align: center;
}
.main-image img {
max-width: 100%;
height: auto;
border-radius: 8px;
}
.main-image p {
text-align: center;
font-size: 0.85rem;
color: #666;
margin-top: 5px;
}
.ingredients {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin: 15px 0;
}
.ingredient {
text-align: center;
}
.ingredient img {
width: 100%;
border-radius: 6px;
}
.ingredient p {
font-size: 0.85rem;
margin-top: 3px;
text-align: center;
}
.highlight {
background-color: #f5f5f5;
padding: 12px;
margin: 15px 0;
border-radius: 6px;
}
ul {
padding-left: 20px;
margin-bottom: 15px;
}
li {
margin-bottom: 8px;
font-size: 0.95rem;
}
strong {
font-weight: 500;
}
</style>
</head>
<body>
<h1>Furikake: Taburan Lezat untuk Nasi!</h1>
<div class="kanji">振り掛け</div>
<p>Tahukah kamu bahwa di Jepang ada bumbu tabur yang
bisa membuat nasi menjadi lebih enak? Namanya Furikake! Kata "furikake" berasal dari bahasa Jepang,
yaitu "furi" yang berarti menaburkan dan "kake" yang berarti menuangkan.
Jadi, furikake adalah bumbu yang ditaburkan di atas nasi supaya lebih lezat!</p>
""",
),
),
);
},
);
},
),
),
),
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children:
filteredCollections.map((item) {
return CollectionContainer(
label: item["label"]!,
imageSvg: item["image"]!,
iconw: 20,
iconh: 20,
width: 70,
height: 70,
textColor: Colors.black,
lblSize: 10,
colorContiner: ColorHelper.hexToColor(
item["color"],
),
onTapAc:
() => {
searchController.clear(),
filterByCategory(item["category"]),
},
);
}).toList(),
),
],
),
),
),
// Bottom Navigation
// MainMenu(),
],
),
);
// Collection Items dengan Filter
SizedBox(height: 20),
Expanded(
child: Container(
decoration: BoxDecoration(
color: const Color.fromARGB(223, 200, 200, 206),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(25),
topRight: Radius.circular(25),
),
),
child: Column(
children: [
SizedBox(height: 5),
Padding(
padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width / 4,
),
child: Divider(
color: const Color.fromARGB(255, 255, 255, 255),
thickness: 2,
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
// vertical: 10,
),
child:
filteredItems.isEmpty
? const Center(
child: Text(
"Tidak ada hasil",
style: TextStyle(
fontSize: 16,
color: Colors.black54,
),
),
)
: Padding(
padding: EdgeInsets.only(
top: 10.0,
left: 10,
right: 10,
),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
color: const Color.fromARGB(
255,
222,
181,
133,
),
),
child: Padding(
padding: EdgeInsets.only(
top: 12,
right: 20,
left: 20,
),
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1, // 2 Kolom
crossAxisSpacing: 10,
mainAxisSpacing: 10,
childAspectRatio: 2.5,
),
itemCount: _listContent.length,
itemBuilder: (context, index) {
return GestureDetector(
child: CardList(
title:
_listContent[index]["title"] ??
"",
body:
_listContent[index]["body"] ??
"",
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder:
(
context,
) => listDetailExtension.buildPage({
'title':
filteredItems[index]['category'],
'imagePath':
filteredItems[index]['image'],
'filepath':
filteredItems[index]['file'],
'paragraphs':
"""
""" +
"<h1> ${_listContent[index]["title"]} </h1> <p> ${_listContent[index]["body"]}",
}),
),
);
},
);
},
),
),
),
),
),
),
],
),
),
),
],
),
);
} else {
return const Center(
child: Padding(
padding: EdgeInsets.all(40),
child: Text("WebView tidak tersedia untuk platform ini."),
),
);
}
}
}
+55 -2
View File
@@ -1,16 +1,19 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_html/flutter_html.dart';
class ListDetailScreen extends StatefulWidget {
final String title;
final String imagePath;
final String paragraphs;
final String filepath;
const ListDetailScreen({
Key? key,
required this.title,
required this.imagePath,
required this.paragraphs,
required this.filepath,
}) : super(key: key);
@override
@@ -19,11 +22,13 @@ class ListDetailScreen extends StatefulWidget {
class _ListDetailScreenState extends State<ListDetailScreen> {
late List<bool> _readStatus;
String _htmlContent = '';
@override
void initState() {
super.initState();
_readStatus = List.generate(widget.paragraphs.length, (index) => false);
loadHtml();
}
void _toggleReadStatus(int index) {
@@ -32,6 +37,15 @@ class _ListDetailScreenState extends State<ListDetailScreen> {
});
}
Future<void> loadHtml() async {
String html = await DefaultAssetBundle.of(
context,
).loadString(widget.filepath);
setState(() {
_htmlContent = html;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -85,7 +99,7 @@ class _ListDetailScreenState extends State<ListDetailScreen> {
// ),
const SizedBox(height: 0),
Expanded(
child: Padding(
child: SingleChildScrollView(
padding: const EdgeInsets.only(left: 18.0, right: 18, bottom: 18),
child:
// ListView.builder(
@@ -156,7 +170,46 @@ class _ListDetailScreenState extends State<ListDetailScreen> {
// ),
Html(
style: {"body": Style(color: const Color.fromRGBO(0, 0, 0, 1))},
data: widget.paragraphs,
// data: widget.paragraphs,
data: _htmlContent,
extensions: [
TagExtension(
tagsToExtend: {"img"},
builder: (ExtensionContext context) {
final attrs = context.attributes;
final src = attrs['src'] ?? '';
if (src.isNotEmpty) {
if (src.startsWith('assets/html/')) {
return Image.asset(
src,
width: 100,
errorBuilder:
(context, error, stackTrace) =>
const Text('Image not found'),
);
} else if (!src.startsWith('http')) {
return Image.asset(
'assets/images/$src',
width: 100,
errorBuilder:
(context, error, stackTrace) =>
const Text('Image not found'),
);
} else {
return Image.network(
src,
width: 100,
errorBuilder:
(context, error, stackTrace) =>
const Text('Image failed to load'),
);
}
}
return const SizedBox();
},
),
],
),
),
),
+15 -7
View File
@@ -18,6 +18,8 @@ class AkunSaya extends StatefulWidget {
class _AkunSayaState extends State<AkunSaya> {
dynamic _profileImage;
dynamic _headerImage;
ImageProvider? imageProvider;
final ImagePicker _picker = ImagePicker();
Future<void> _pickImage(bool isProfile) async {
@@ -35,13 +37,19 @@ class _AkunSayaState extends State<AkunSaya> {
} else {
final pickedFile = await _picker.pickImage(source: ImageSource.gallery);
if (pickedFile != null) {
final imageFile = File(pickedFile.path);
setState(() {
if (isProfile) {
_profileImage = File(pickedFile.path);
_profileImage = imageFile;
} else {
_headerImage = File(pickedFile.path);
_headerImage = imageFile;
}
imageProvider =
kIsWeb ? MemoryImage(_profileImage) : FileImage(_profileImage);
});
} else {
imageProvider = AssetImage("assets/images/luffy.png");
}
}
}
@@ -97,7 +105,7 @@ class _AkunSayaState extends State<AkunSaya> {
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 40),
children: [
TextFormField(
initialValue: "Cepot",
initialValue: "Luffy Kun",
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Nama',
@@ -106,7 +114,7 @@ class _AkunSayaState extends State<AkunSaya> {
style: TextStyle(color: Colors.black),
),
TextFormField(
initialValue: "cpt09",
initialValue: "luffy01",
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Nama Pengguna',
@@ -169,7 +177,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(
@@ -184,14 +192,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,
+28 -10
View File
@@ -24,6 +24,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
// final ImagePicker _picker = ImagePicker();
dynamic _profileImage;
dynamic _headerImage;
ImageProvider? imageProvider;
final ImagePicker _picker = ImagePicker();
Future<void> _pickImage(bool isProfile) async {
@@ -41,13 +42,19 @@ class _ProfileScreenState extends State<ProfileScreen> {
} else {
final pickedFile = await _picker.pickImage(source: ImageSource.gallery);
if (pickedFile != null) {
final imageFile = File(pickedFile.path);
setState(() {
if (isProfile) {
_profileImage = File(pickedFile.path);
_profileImage = imageFile;
} else {
_headerImage = File(pickedFile.path);
_headerImage = imageFile;
}
imageProvider =
kIsWeb ? MemoryImage(_profileImage) : FileImage(_profileImage);
});
} else {
imageProvider = AssetImage("assets/images/luffy.png");
}
}
}
@@ -135,10 +142,8 @@ class _ProfileScreenState extends State<ProfileScreen> {
: null,
child:
_profileImage == null
? Icon(
Icons.person,
size: 50,
color: Colors.white,
? Image.asset(
"assets/images/luffy.png",
)
: null,
),
@@ -162,9 +167,16 @@ class _ProfileScreenState extends State<ProfileScreen> {
Row(
children: [
Text(
"Name",
"Luffy",
style: TextStyle(
color: Colors.blueAccent,
color: const Color.fromARGB(
255,
0,
0,
0,
),
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
],
@@ -172,9 +184,15 @@ class _ProfileScreenState extends State<ProfileScreen> {
Row(
children: [
Text(
"Edit Profile",
"Luffy Kun",
style: TextStyle(
color: Colors.blueAccent,
color: const Color.fromARGB(
255,
0,
0,
0,
),
fontWeight: FontWeight.normal,
),
),
],