diff --git a/lib/components/card_list.dart b/lib/components/card_list.dart index b9d4523..49740d2 100644 --- a/lib/components/card_list.dart +++ b/lib/components/card_list.dart @@ -1,7 +1,10 @@ import 'package:flutter/material.dart'; class CardList extends StatelessWidget { - const CardList({super.key}); + final String title; + final String body; + + const CardList({super.key, required this.title, required this.body}); @override Widget build(BuildContext context) { @@ -19,7 +22,7 @@ class CardList extends StatelessWidget { 10.0, ), // Opsional: membuat gambar lebih rounded child: Image.asset( - 'assets/images/cepott.png', // Ganti dengan path gambar Anda + 'assets/images/cepott.png', width: 80, // Atur ukuran gambar height: 120, fit: BoxFit.cover, @@ -31,17 +34,18 @@ class CardList extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Judul Card", + this.title, style: const TextStyle( - fontSize: 16, + fontSize: 14, fontWeight: FontWeight.bold, ), ), - const SizedBox(height: 5, width: 10), + const SizedBox(height: 1, width: 5), Text( - "Deskripsi singkat tentang item ini. karena ini sangat panjang maka ini akan dipotong", + body, style: const TextStyle( - fontSize: 12, + letterSpacing: 0.2, + fontSize: 14, fontWeight: FontWeight.normal, ), maxLines: 2, diff --git a/lib/components/main_menu.dart b/lib/components/main_menu.dart index afff742..5c572a6 100644 --- a/lib/components/main_menu.dart +++ b/lib/components/main_menu.dart @@ -44,7 +44,7 @@ class _MainMenuState extends State { Provider.of(context).selectedIndex; return Container( color: Colors.transparent, - padding: const EdgeInsets.symmetric(vertical: 10), + padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 5), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisSize: MainAxisSize.min, diff --git a/lib/components/scan_button.dart b/lib/components/scan_button.dart index 283f46b..075b425 100644 --- a/lib/components/scan_button.dart +++ b/lib/components/scan_button.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:furibase/screen/camera_screen.dart'; class ScanButton extends StatelessWidget { const ScanButton({super.key}); @@ -34,7 +35,12 @@ class ScanButton extends StatelessWidget { BlendMode.srcIn, ), ), - onPressed: () {}, + onPressed: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => CameraScreen()), + ); + }, ), ); } diff --git a/lib/screen/camera_screen.dart b/lib/screen/camera_screen.dart new file mode 100644 index 0000000..8085fd7 --- /dev/null +++ b/lib/screen/camera_screen.dart @@ -0,0 +1,99 @@ +import 'dart:io'; + +import 'package:camera/camera.dart'; +import 'package:flutter/material.dart'; +import 'package:permission_handler/permission_handler.dart'; + +class CameraScreen extends StatefulWidget { + @override + _CameraScreenState createState() => _CameraScreenState(); +} + +class _CameraScreenState extends State { + CameraController? controller; + List? cameras; + XFile? capturedImage; + + @override + void initState() { + super.initState(); + initCamera(); + } + + Future initCamera() async { + await Permission.camera.request(); + + if (await Permission.camera.isGranted) { + cameras = await availableCameras(); + controller = CameraController(cameras![0], ResolutionPreset.high); + + await controller!.initialize(); + setState(() {}); // update UI setelah kamera siap + } + } + + @override + void dispose() { + controller?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (controller == null || !controller!.value.isInitialized) { + return Center(child: CircularProgressIndicator()); + } + + return Scaffold( + appBar: AppBar( + title: Text('Camera'), + leading: IconButton( + icon: Icon(Icons.arrow_back), + onPressed: () { + Navigator.pop(context); + }, + ), + ), + body: Stack( + children: [ + CameraPreview(controller!), + if (capturedImage != null) + Positioned.fill( + child: Image.file(File(capturedImage!.path), fit: BoxFit.cover), + ), + Align( + alignment: Alignment.bottomCenter, + child: Padding( + padding: const EdgeInsets.only(bottom: 30), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FloatingActionButton( + backgroundColor: Colors.white, + onPressed: () async { + final image = await controller!.takePicture(); + setState(() { + capturedImage = image; + }); + + Navigator.pop(context, capturedImage?.path); + }, + child: Icon(Icons.camera_alt, color: Colors.black), + ), + ], + ), + ), + ), + ], + ), + // floatingActionButton: FloatingActionButton( + // onPressed: () async { + // final image = await controller!.takePicture(); + // print('Gambar disimpan di: ${image.path}'); + // // Bisa dilanjutkan untuk upload, crop, dll + // }, + // child: Icon(Icons.camera_alt), + // ), + ); + } +} diff --git a/lib/screen/pustaka/list_education.dart b/lib/screen/pustaka/list_education.dart index 6b95557..0e6d3d5 100644 --- a/lib/screen/pustaka/list_education.dart +++ b/lib/screen/pustaka/list_education.dart @@ -70,10 +70,36 @@ class _ListEducationState extends State { "category": "keselamatan", }, ]; + + final List> _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> filteredCollections = _collections .where( - (item) => item["label"]!.toLowerCase().contains( + (item) => item["label"].toLowerCase().contains( _searchQuery.toLowerCase(), ), ) @@ -298,10 +324,17 @@ class _ListEducationState extends State { mainAxisSpacing: 10, childAspectRatio: 2.5, ), - itemCount: filteredItems.length, + itemCount: _listContent.length, itemBuilder: (context, index) { return GestureDetector( - child: CardList(), + child: CardList( + title: + _listContent[index]["title"] ?? + "", + body: + _listContent[index]["body"] ?? + "", + ), onTap: () { Navigator.push( context, @@ -314,7 +347,7 @@ class _ListEducationState extends State { filteredItems[index], imagePath: filteredItems[index], - paragraphs: [ + /* paragraphs: [ """

Enter the main heading, usually the same as the title.

Be bold in stating your key points. Put them in a list:

@@ -329,7 +362,137 @@ class _ListEducationState extends State {

Finally, link to another page in your own Web site.

© Wiley Publishing, 2011

""", - ], + ], */ + paragraphs: """ + + + + + + Furikake: Taburan Lezat untuk Nasi! + + + +

Furikake: Taburan Lezat untuk Nasi!

+ +
振り掛け
+ +

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!

+ """, ), ), ); diff --git a/lib/screen/pustaka/pustaka_detail_screen.dart b/lib/screen/pustaka/pustaka_detail_screen.dart index 263e386..f1c506a 100644 --- a/lib/screen/pustaka/pustaka_detail_screen.dart +++ b/lib/screen/pustaka/pustaka_detail_screen.dart @@ -4,7 +4,7 @@ import 'package:flutter_html/flutter_html.dart'; class ListDetailScreen extends StatefulWidget { final String title; final String imagePath; - final List paragraphs; + final String paragraphs; const ListDetailScreen({ Key? key, @@ -55,95 +55,108 @@ class _ListDetailScreenState extends State { ), ), ), - 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: Text( - widget.title, - style: const TextStyle( - fontSize: 22, - fontWeight: FontWeight.bold, - color: Colors.black, - ), - ), - ), - ], - ), - ), - const SizedBox(height: 16), + + // 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: Padding( - padding: const EdgeInsets.all(18.0), - child: ListView.builder( - itemCount: widget.paragraphs.length, - itemBuilder: (context, index) { - return Padding( - padding: const EdgeInsets.only(bottom: 16.0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: - // Text( - // widget.paragraphs[index] + - // style: TextStyle(color: Colors.black), - Html( - style: {"body": Style(color: Colors.black)}, - data: - widget.paragraphs.toString() + - """ - - - - Enter a title, displayed at the top of the window. - - - -

Enter the main heading, usually the same as the title.

-

Be bold in stating your key points. Put them in a list:

-
    -
  • The first item in your list
  • -
  • The second item; italicize key words
  • -
-

Improve your image by including an image.

-

A Great HTML Resource

-

Add a link to your favorite Web site. - Break up your page with a horizontal rule or two.

-
-

Finally, link to another page in your own Web site.

- -

© Wiley Publishing, 2011

- - - """, - ), - ), - // ), - // Checkbox( - // value: _readStatus[index], - // onChanged: (bool? value) { - // _toggleReadStatus(index); - // }, - // ), - ], - ), - ); - }, + 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 + + // """ + // + // + // + // Enter a title, displayed at the top of the window. + // + // + // + //

Enter the main heading, usually the same as the title.

+ //

Be bold in stating your key points. Put them in a list:

+ //
    + //
  • The first item in your list
  • + //
  • The second item; italicize key words
  • + //
+ //

Improve your image by including an image.

+ //

A Great HTML Resource

+ //

Add a link to your favorite Web site. + // Break up your page with a horizontal rule or two.

+ //
+ //

Finally, link to another page in your own Web site.

+ // + //

© Wiley Publishing, 2011

+ // + // + // """, + // ), + // ), + // // ), + // // Checkbox( + // // value: _readStatus[index], + // // onChanged: (bool? value) { + // // _toggleReadStatus(index); + // // }, + // // ), + // ], + // ), + // ); + // }, + // ), + Html( + style: {"body": Style(color: const Color.fromRGBO(0, 0, 0, 1))}, + data: widget.paragraphs, ), ), ), diff --git a/pubspec.lock b/pubspec.lock index 5e3b2d7..a912fe7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -33,6 +33,46 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + camera: + dependency: "direct main" + description: + name: camera + sha256: dfa8fc5a1adaeb95e7a54d86a5bd56f4bb0e035515354c8ac6d262e35cec2ec8 + url: "https://pub.dev" + source: hosted + version: "0.10.6" + camera_android: + dependency: transitive + description: + name: camera_android + sha256: bd9737671a00d979e0310a946e5be2fdc621b6a36b95378755cb3e4498e61485 + url: "https://pub.dev" + source: hosted + version: "0.10.10+2" + camera_avfoundation: + dependency: transitive + description: + name: camera_avfoundation + sha256: a33cd9a250296271cdf556891b7c0986a93772426f286595eccd5f45b185933c + url: "https://pub.dev" + source: hosted + version: "0.9.18+14" + camera_platform_interface: + dependency: transitive + description: + name: camera_platform_interface + sha256: "2f757024a48696ff4814a789b0bd90f5660c0fb25f393ab4564fb483327930e2" + url: "https://pub.dev" + source: hosted + version: "2.10.0" + camera_web: + dependency: transitive + description: + name: camera_web + sha256: "595f28c89d1fb62d77c73c633193755b781c6d2e0ebcd8dc25b763b514e6ba8f" + url: "https://pub.dev" + source: hosted + version: "0.3.5" characters: dependency: transitive description: @@ -416,6 +456,54 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + sha256: "59adad729136f01ea9e35a48f5d1395e25cba6cea552249ddbe9cf950f5d7849" + url: "https://pub.dev" + source: hosted + version: "11.4.0" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: d3971dcdd76182a0c198c096b5db2f0884b0d4196723d21a866fc4cdea057ebc + url: "https://pub.dev" + source: hosted + version: "12.1.0" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023 + url: "https://pub.dev" + source: hosted + version: "9.4.7" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" + url: "https://pub.dev" + source: hosted + version: "0.1.3+5" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878 + url: "https://pub.dev" + source: hosted + version: "4.3.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" + url: "https://pub.dev" + source: hosted + version: "0.2.1" petitparser: dependency: transitive description: @@ -533,6 +621,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + url: "https://pub.dev" + source: hosted + version: "2.1.1" string_scanner: dependency: transitive description: @@ -678,37 +774,37 @@ packages: source: hosted version: "1.1.0" webview_flutter: - dependency: transitive + dependency: "direct main" description: name: webview_flutter - sha256: "889a0a678e7c793c308c68739996227c9661590605e70b1f6cf6b9a6634f7aec" + sha256: caf0f5a1012aa3c2d33c4215adc72dc1194bb59a2d3ed901f457965626805e66 url: "https://pub.dev" source: hosted - version: "4.10.0" + version: "4.11.0" webview_flutter_android: dependency: transitive description: name: webview_flutter_android - sha256: "512c26ccc5b8a571fd5d13ec994b7509f142ff6faf85835e243dde3538fdc713" + sha256: "5c3b6f992d123084903ec091b84f021c413a92a9af49038e4564a1b26c8452cf" url: "https://pub.dev" source: hosted - version: "4.3.2" + version: "4.4.1" webview_flutter_platform_interface: dependency: transitive description: name: webview_flutter_platform_interface - sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d + sha256: "18b1640839cf6546784a524c72aded5b6e86b23e7167dc2311cc96f7658b64bd" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" webview_flutter_wkwebview: dependency: transitive description: name: webview_flutter_wkwebview - sha256: d7403ef4f042714c9ee2b26eaac4cadae7394cb0d4e608b1dd850c3ff96bd893 + sha256: c9f9be526fa0d3347374ceaa05c4b3acb85f4f112abd62f7d74b7d301fa515ff url: "https://pub.dev" source: hosted - version: "3.18.2" + version: "3.20.0" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 64f5578..34cc4be 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,7 +30,8 @@ environment: dependencies: flutter: sdk: flutter - + camera: ^0.10.5+5 + permission_handler: ^11.3.0 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 @@ -44,6 +45,7 @@ dependencies: image_picker_for_web: ^3.0.6 image_picker_web: ^4.0.0 flutter_plugin_android_lifecycle: ^2.0.9 + webview_flutter: ^4.11.0 # arcore_flutter_plugin: ^0.2.0-alpha # flutter_unity_widget: ^2022.2.1 # arcore_flutter_plugin: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 043a96f..4e586f9 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,11 +7,14 @@ #include "generated_plugin_registrant.h" #include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { FileSelectorWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("FileSelectorWindows")); + PermissionHandlerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index a95e267..1119879 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_windows + permission_handler_windows url_launcher_windows )