https://github.com

https://codeium.com

https://idx.google.com

MObile LEarning (mole)

Extension

main.dart

import 'package:flutter/material.dart';

Future<void> main() async {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'MOLE IT',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const Home(),
    );
  }
}

class Home extends StatelessWidget {
  const Home({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Container(
            color: Colors.purple,
            height: 100,
            width: double.infinity,
            child: const Center(
                child: Text('purple', style: TextStyle(color: Colors.white))),
          ),
        ],
      ),
    );
  }
}

Pecah menjadi 2 File

import 'package:flutter/material.dart';

class Home extends StatelessWidget {
  const Home({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Container(
            color: Colors.purple,
            height: 100,
            width: double.infinity,
            child: const Center(
                child: Text('purple', style: TextStyle(color: Colors.white))),
          ),
        ],
      ),
    );
  }
}
import 'package:flutter/material.dart';
import 'package:myapp/page/home.dart';

Future<void> main() async {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'MOLE IT',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const Home(),
    );
  }
}

page/home.dart

main.dart

Color

import 'package:flutter/material.dart';

class Home extends StatelessWidget {
  const Home({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Container(
            color: Colors.purple,
            height: 100,
            width: double.infinity,
            child: const Center(
                child: Text('purple', style: TextStyle(color: Colors.white))),
          ),
          Container(
            color: Colors.blue.withOpacity(0.5),
            height: 100,
            width: double.infinity,
            child: const Center(
                child: Text('Blue with opacity',
                    style: TextStyle(color: Colors.white))),
          ),
          Container(
            color: Colors.red.shade700,
            height: 100,
            width: double.infinity,
            child: const Center(
                child: Text('Red with shade 700',
                    style: TextStyle(color: Colors.white))),
          ),
          Container(
            color: Colors.green[100],
            height: 100,
            width: double.infinity,
            child: const Center(
                child: Text('Green from palette',
                    style: TextStyle(color: Colors.white))),
          ),
          Container(
            color:
                Colors.yellow[800], // Warna kuning dari palet dengan indeks 800
            height: 100,
            width: double.infinity,
            child: const Center(
                child: Text('Yellow from palette',
                    style: TextStyle(color: Colors.black))),
          ),
        ],
      ),
    );
  }
}

Text

import 'package:flutter/material.dart';

class Home extends StatelessWidget {
  const Home({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
      ),
      body: const Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'Normal Text',
              style: TextStyle(fontSize: 20),
            ),
            SizedBox(height: 20),
            Text(
              'Bold Text',
              style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
            ),
            SizedBox(height: 20),
            Text(
              'Italic Text',
              style: TextStyle(fontSize: 20, fontStyle: FontStyle.italic),
            ),
            SizedBox(height: 20),
            Text(
              'Underlined Text',
              style:
                  TextStyle(fontSize: 20, decoration: TextDecoration.underline),
            ),
            SizedBox(height: 20),
            Text(
              'Text with Color',
              style: TextStyle(fontSize: 20, color: Colors.blue),
            ),
            SizedBox(height: 20),
            Text(
              'Custom Font Family',
              style: TextStyle(fontSize: 20, fontFamily: 'arial'),
            ),
          ],
        ),
      ),
    );
  }
}

Splash Screen

upload image ke folder
assets/images/splash.png

aktifkan asset di file pubspec.yaml

Splash Screen

import 'package:flutter/material.dart';

class Splash extends StatefulWidget {
  const Splash({super.key});

  @override
  State<Splash> createState() => _SplashState();
}

class _SplashState extends State<Splash> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: Container(
        margin: const EdgeInsets.all(16),
        decoration: const BoxDecoration(
          image: DecorationImage(
            image: AssetImage('assets/images/splash.png'),
            fit: BoxFit.contain,
          ),
        ),
      ),
    );
  }
}

Update

Tambahkan package get: ^4.6.6

import 'package:flutter/material.dart';
import 'package:get/get_navigation/src/root/get_material_app.dart';

import 'page/splash.dart';

Future<void> main() async {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'MOLE IT',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const Splash(),
    );
  }
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'home.dart';

class Splash extends StatefulWidget {
  const Splash({super.key});

  @override
  State<Splash> createState() => _SplashState();
}

class _SplashState extends State<Splash> {
  @override
  void initState() {
    super.initState();
    Future.delayed(const Duration(seconds: 3), () {
      Get.off(const Home());
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: Container(
        margin: const EdgeInsets.all(16),
        decoration: const BoxDecoration(
          image: DecorationImage(
            image: AssetImage('assets/images/splash.png'),
            fit: BoxFit.contain,
          ),
        ),
      ),
    );
  }
}

page/splash.dart

main.dart

push to github

  • initial repository
  • commit
  • publish branch

User Interface

tampilan visual sebuah produk yang menjembatani sistem dengan pengguna (user). Tampilan UI dapat berupa bentuk, warna, dan tulisan yang didesain semenarik mungkin. Secara sederhana, UI adalah bagaimana tampilan sebuah produk dilihat oleh pengguna.

User Experience

bagaimana pengalaman pengguna dalam berinteraksi/menggunakan produk digital Anda. Pengalaman ini dilihat dari betapa mudahnya pengguna untuk mendapatkan apa yang mereka inginkan dari produk tersebut. Dengan kata lain, user experience produk yang bagus tidak akan menyulitkan pengguna untuk mencapai tujuan mereka. Entah itu dari desain UI yang friendly, produk yang ringan untuk diakses, menu yang tidak berbelit-belit, dan lain sebagainya.

Perbedaan UI & UX

Menemukan Masalah:

Seorang UX Designer, akan memperhatikan perilaku pengemudi dan memahami bahwa tidaklah mudah untuk mengemudikan mobil dengan pasak kemudi itu. Hal ini menjadi perhatian UX designer

Evolusi kemudi mobil sebelum masa perang menjadi setir seperti yang sekarang kita kenal Dalam kasus setir mobil ini, UI designer akan bekerja sama dengan UX designer mendesain bagaimana antarmuka setir tersebut bisa berfungsi sesuai dengan konsep yang dikembangkan.

Laws of UX

kumpulan praktik terbaik yang dapat dipertimbangkan UI Designer saat membangun tampilan dari sebuah sistem. Hukum-hukum yang ada akan berkaitan satu sama lain untuk menunjang terwujudnya kepuasan pengguna

Aesthetic Usability Effect

Pengguna itu ternyata sering menganggap bahwa desain yang bermanfaat itu desain yang estetis. Jadi gampangnya, semakin estetis, semakin bermanfaat aplikasi tersebut. Desain yang lebih menyenangkan secara estetika itu dapat membuat pengguna lebih toleran terhadap masalah-masalah sepele di aplikasi tersebut.

Fitts Law

Hukum UX yang bernama Fitts’s Law ini menyatakan bahwa waktu untuk mendapatkan target adalah soal jarak ke target dan ukuran target. Artinya desain dari suatu tampilan harus mudah membuat penggunanya menemukan dan melakukan interaksi pada tools atau bagian tertentu yang dituju dalam waktu yang relatif cepat.

Goal-Gradient Effect

Sebuah tujuan akan cenderung lebih ingin dicapai ketika tujuan tersebut sudah berada lebih dekat.
Desain yang mendukung terwujudnya goal-gradient effect adalah dengan membuat tampilan yang merepresentasikan progress dan penyelesaian yang membuat pengguna termotivasi menyelesaikan aktivitas, contohnya adalah progress bar.

Hick’s Law

Hick’s Law ini menyatakan bahwa waktu yang diperlukan untuk membuat keputusan meningkat seiring dengan banyaknya jumlah dan kompleksitas pilihan

Jakob’s Law

Hukum yang satu ini menyatakan bahwa ternyata pengguna menghabiskan sebagian besar waktunya di aplikasi lain.Itu artinya pengguna lebih suka aplikasi yang kita buat bekerja dengan cara yang sama seperti semua aplikasi lain yang sudah mereka (pengguna) ketahui.

Doherty Threshold

Doherty Threshold hukum UX yang mengatakan kalau ternyata produktivitas bisa melonjak ketika komputer dan penggunanya berinteraksi dengan kecepatan kurang dari 400ms (millisecond).

Salah satu cara yang dapat dimanfaatkan untuk dapat membuat pengguna merasa lebih sabar untuk menunggu adalah dengan pemanfaatan animasi. Selain itu, tampilan progress bar dapat membantu toleransi waktu tunggu pengguna. Kamu bisa lihat grafik berikut

Occam’s Razor

hipotesis yang bersaing yang memprediksi dengan baik, yang memiliki asumsi paling sedikit harus dipilih. Maka dari itu, kamu sebaiknya analisis setiap elemen dan hapus sebanyak mungkin, tanpa mengganggu fungsinya secara keseluruhan.

Miller’s Law

Hukum ini cukup unik. Kok bisa? Ia mengatakan bahwa ternyata rata-rata orang hanya dapat menyimpan 7 (plus atau minus 2) item dalam memori kerja mereka.

Parkinson’s Law

Parkinson’s Law mencoba untuk menjelaskan kepada kita tentang pandangan pengguna dalam pengoperasian produk. Mencoba untuk mengerti atau menghitung jumlah waktu yang harus dikerjakan oleh user ketika melakukan pengoperasian produk yang telah kita desain. Semakin cepat mereka memahami semakin baik desain yang telah kita kerjakan.

Pareto Principle

Kalau konteksnya kita sedang membuat UX dalam sebuah aplikasi, maka fokuslah pada 20% dari keseluruhan fitur aplikasi tersebut. Dimana 20% fitur tersebut adalah fitur yang punya dampak paling besar.

Postel’s Law

Hukum yang namanya Postel’s Law mengatakan untuk liberal dalam apa yang kita terima, dan konservatif dalam apa yang kita kirim. Ketika kita membuat sebuah formulir untuk pengguna, kita harus menerima variabel jawaban tertentu dalam formulir tersebut.

Tesler’s Law

Setiap sistem itu ada sejumlah kompleksitas yang tidak bisa dikurangi. Terkadang untuk membuat desain yang bagus, maka designer perlu untuk membuat proses menjadi rumit, namun pengguna merasa tidak terlalu rumit dikarenakan adanya desain yang menarik.

Law of Common Region

Hukum ini mengatakan bahwa elemen dapat dikelompokkan jika mereka dibagi oleh area dengan batas yang jelas

Law of Proximity

objek/elemen yang berdekatan satu sama lain, cenderung dikelompokkan bersama.

Objek atau elemen yang saling berdekatan ini membantu pengguna dalam memahami dan mengatur informasi lebih cepat dan lebih efisien.

 Law of Prägnanz

Hukum ini mengungkapkan bahwa mata manusia suka menemukan kesederhanaan dan ‘keteraturan dalam bentuk yang rumit’ karena mencegah kita dari kewalahan dengan informasi.

Law of Similarity

Mata manusia cenderung mempersepsikan elemen-elemen serupa dalam suatu desain sebagai gambar, bentuk, atau kelompok yang lengkap, bahkan jika elemen-elemen itu dipisahkan.

Law of Uniform Connectedness

hukum ini mengatakan kalau elemen yang terhubung secara visual itu dianggap lebih terkait/berhubungan daripada elemen tanpa koneksi.

Peak-End Rule

Ternyata pengguna itu cenderung menilai suatu pengalaman (experience) berdasarkan bagaimana perasaan mereka pada puncaknya (Peak moment) dan pada akhirnya (End moment), dibandingkan jumlah total atau rata-rata setiap momen pengalaman. Aplikasi Duolingo juga memberi selamat kepada pengguna untuk menjawab 10 pertanyaan dengan benar. Tentu pengalaman pengguna seperti ini yang tidak akan dilupakan.

Serial Position Effect

Pengguna memiliki kecenderungan untuk mengingat item pertama dan terakhir secara berurutan. Instagram, Twitter, Medium menempatkan 2 menu navigasi terpenting di pojok kanan dan pojok kiri. Di pojok kanan ada menu ‘Akun/profil’. Sedangkan di pojok kiri ada menu ‘Beranda’

Von Restorff Effect

Ketika beberapa objek serupa hadir, salah satu yang berbeda dari yang lain kemungkinan besar akan diingat.

Zeigarnik Effect

pengguna itu lebih gampang mengingat tugas/hal yang tidak selesai atau terganggu. Nah itulah kenapa kadang kalau kamu melakukan hal sesuatu di sebuah aplikasi itu ada ‘progress bar’-nya

Praktek Figma

pubspec.yaml

name: myapp
description: "A new Flutter project."

publish_to: 'none' 

version: 1.0.0+1

environment:
  sdk: ^3.5.3

dependencies:
  flutter:
    sdk: flutter

  get: ^4.6.6

  cupertino_icons: ^1.0.8

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:

  uses-material-design: true

  assets:
    - assets/images/
    - assets/database.json



color.dart

const String appName = 'Mobile Learning';

const String imageSplash = 'assets/images/splash.png';

app_config.dart

const String appName = 'Mobile Learning';

const String imageSplash = 'assets/images/splash.png';

my_button.dart

import '../core.dart';
class MyButton extends StatelessWidget {
  final String buttonText;
  final Color buttonColor;
  final Color textColor;
  final VoidCallback? onPressed;

  const MyButton({
    Key? key,
    required this.buttonText,
    this.buttonColor = primaryColor,
    this.textColor = whiteColor,
    this.onPressed,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: onPressed ??
          () => Get.off(const Home()), // Default to navigating to Home
      style: ElevatedButton.styleFrom(
        backgroundColor: buttonColor,
        padding: const EdgeInsets.symmetric(
          horizontal: 32,
          vertical: 12,
        ),
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(12),
        ),
      ),
      child: Text(
        buttonText,
        style: TextStyle(
          fontSize: 16,
          color: textColor,
        ),
      ),
    );
  }
}

my_card.dart

import '../core.dart';

class MyCard extends StatelessWidget {
  const MyCard({
    super.key,
    required this.title,
    required this.description,
    required this.imagePath, // Add imagePath parameter
  });

  final dynamic title;
  final dynamic description;
  final String imagePath; // Declare the imagePath parameter

  @override
  Widget build(BuildContext context) {
    return Card(
      color: whiteColor,
      elevation: 4,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(12),
      ),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Image.asset(
            imagePath, // Use dynamic image path here
            width: 100,
            height: 100,
          ),
          const SizedBox(height: 10),
          Text(
            title,
            style: const TextStyle(
              fontSize: 18,
              fontWeight: FontWeight.bold,
            ),
            textAlign: TextAlign.center,
          ),
          const SizedBox(height: 5),
          Text(
            description,
            style: const TextStyle(
              fontSize: 14,
              color: Colors.grey,
            ),
            textAlign: TextAlign.center,
          ),
        ],
      ),
    );
  }
}

my_container.dart

import '../core.dart';

class MyContainer extends StatelessWidget {
  final List<Widget> children;
  final Color? containerColor; // Added color parameter
  final EdgeInsetsGeometry? margin; // Added margin parameter
  final EdgeInsetsGeometry? padding; // Added padding parameter
  final BorderRadiusGeometry
      borderRadius; // Added dynamic borderRadius parameter
  final MainAxisAlignment?
      mainAxisAlignment; // Added dynamic mainAxisAlignment parameter
  final CrossAxisAlignment?
      crossAxisAlignment; // Added dynamic crossAxisAlignment parameter

  const MyContainer({
    Key? key,
    required this.children,
    this.containerColor = Colors.white, // Default color is white
    this.margin = const EdgeInsets.all(16), // Default margin
    this.padding = const EdgeInsets.all(16), // Default padding
    this.borderRadius = const BorderRadius.all(
        Radius.circular(25)), // Default border radius is 25 for all corners
    this.mainAxisAlignment =
        MainAxisAlignment.start, // Default mainAxisAlignment is start
    this.crossAxisAlignment =
        CrossAxisAlignment.start, // Default crossAxisAlignment is start
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      margin: margin, // Use dynamic margin here
      padding: padding, // Use dynamic padding here
      decoration: BoxDecoration(
        color: containerColor, // Use dynamic color here
        borderRadius: borderRadius, // Use dynamic border radius here
      ),
      child: Column(
        mainAxisAlignment: mainAxisAlignment ??
            MainAxisAlignment.center, // Use dynamic mainAxisAlignment
        crossAxisAlignment: crossAxisAlignment ??
            CrossAxisAlignment.center, // Use dynamic crossAxisAlignment
        children: children,
      ),
    );
  }
}

my_text.dart

import '../core.dart';

class MyText extends StatelessWidget {
  final String titleText;
  final TextStyle? titleStyle;
  final TextAlign textAlign;

  const MyText({
    Key? key,
    required this.titleText,
    this.titleStyle,
    this.textAlign = TextAlign.left, // Default alignment is set to left
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(
      titleText,
      textAlign: textAlign,
      style: titleStyle ??
          const TextStyle(
            fontSize: 22,
            fontWeight: FontWeight.bold,
            color: Colors.black,
          ),
    );
  }
}

core.dart

export 'package:flutter/material.dart';
export 'package:get/get.dart';
export 'dart:convert';
export 'package:flutter/services.dart' show rootBundle;

export 'utils/colors.dart';
export 'utils/app_config.dart';
export 'widget/my_text.dart';
export 'widget/my_button.dart';
export 'widget/my_container.dart';
export 'widget/my_card.dart';

export 'pages/splash.dart';
export 'pages/home.dart';

main.dart

import 'core.dart';

Future<void> main() async {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Mobile Learning',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: primaryColor),
        useMaterial3: true,
      ),
      home: const Splash(),
    );
  }
}

splash.dart

import '../core.dart';

class Splash extends StatelessWidget {
  const Splash({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: primaryColor,
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          const Expanded(
            flex: 3,
            child: ImageSection(),
          ),
          Expanded(
            flex: 2,
            child: MainContainer(),
          ),
        ],
      ),
    );
  }
}

class ImageSection extends StatelessWidget {
  const ImageSection({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      child: Image.asset(
        imageSplash,
        fit: BoxFit.contain,
      ),
    );
  }
}

class MainContainer extends StatelessWidget {
  const MainContainer({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return MyContainer(children: [
      Center(
        child: MyText(
          titleText: 'Discover your next skill\nLearn anything you want!',
          titleStyle: TextStyle(
            color: blackColor,
            fontSize: 24,
            fontWeight: FontWeight.bold,
          ),
        ),
      ),
      const SizedBox(height: 5),
      Center(
        child: MyText(
          titleText: 'Discover your next skill\nLearn anything you want!',
          titleStyle: TextStyle(
            color: Colors.black54,
            fontSize: 16,
            fontWeight: FontWeight.normal,
          ),
        ),
      ),
      const SizedBox(height: 20),
      Center(
        child: MyButton(
          buttonText: 'Get Started',
          buttonColor: primaryColor,
          textColor: whiteColor,
          onPressed: () {
            Get.off(const Home());
          },
        ),
      ),
    ]);
  }
}

home.dart

import '../core.dart';

class Home extends StatefulWidget {
  const Home({super.key});

  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  TextEditingController _searchController = TextEditingController();
  List<dynamic> _filteredData = [];
  List<dynamic> _originalData = [];

  Future<List<dynamic>> loadJsonData() async {
    try {
      final String response =
          await rootBundle.loadString('assets/database.json');
      return json.decode(response);
    } catch (error) {
      throw Exception('Failed to load JSON data: $error');
    }
  }

  void _filterData(String query) {
    if (query.isEmpty || query.length < 3) {
      setState(() {
        // If query is less than 3 characters, show the original data
        _filteredData = _originalData;
      });
      return;
    }

    setState(() {
      _filteredData = _originalData.where((item) {
        String title = item['title'] ?? '';
        String description = item['description'] ?? '';
        return title.toLowerCase().contains(query.toLowerCase()) ||
            description.toLowerCase().contains(query.toLowerCase());
      }).toList();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: whiteColor,
      body: FutureBuilder<List<dynamic>>(
        future: loadJsonData(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          } else if (snapshot.hasError) {
            return Center(child: Text('Error: ${snapshot.error}'));
          }

          if (!snapshot.hasData || snapshot.data!.isEmpty) {
            return const Center(child: Text('No data available.'));
          }

          final List<dynamic> data = snapshot.data!;

          // Store original data to reset the filtered data when necessary
          _originalData = data;
          _filteredData = data;

          return Column(
            children: [
              // Header Section with search input
              MyHeader(
                  searchController: _searchController,
                  onSearchChanged: _filterData),
              const SizedBox(height: 20),
              // Categories Section
              CategoryHeader(),
              CategoryBody(data: _filteredData),
              // Bottom Navigation Bar
              BottomNavBar(),
            ],
          );
        },
      ),
    );
  }
}

class MyHeader extends StatelessWidget {
  const MyHeader({
    super.key,
    required this.searchController,
    required this.onSearchChanged,
  });

  final TextEditingController searchController;
  final Function(String) onSearchChanged;

  @override
  Widget build(BuildContext context) {
    return MyContainer(
      containerColor: primaryColor,
      margin: EdgeInsets.all(0),
      borderRadius: BorderRadius.only(
        bottomLeft: Radius.circular(25),
        bottomRight: Radius.circular(25),
      ),
      children: [
        const SizedBox(height: 20),
        MyText(
          titleText: 'Hello',
          titleStyle: TextStyle(
            color: whiteColor,
            fontSize: 24,
            fontWeight: FontWeight.bold,
          ),
        ),
        MyText(
          titleText: 'Good Morning',
          titleStyle: TextStyle(
            color: whiteColor,
            fontSize: 18,
            fontWeight: FontWeight.normal,
          ),
        ),
        const SizedBox(height: 20),
        Container(
          padding: const EdgeInsets.symmetric(horizontal: 16),
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.circular(30),
          ),
          child: TextField(
            controller: searchController,
            onChanged: onSearchChanged,
            decoration: InputDecoration(
              hintText: 'Search your topic',
              border: InputBorder.none,
              icon: Icon(Icons.search, color: Colors.grey[600]),
            ),
          ),
        ),
      ],
    );
  }
}

class CategoryHeader extends StatelessWidget {
  const CategoryHeader({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          MyText(
            titleText: 'Explore Categories',
            titleStyle: TextStyle(
              fontSize: 20,
              fontWeight: FontWeight.bold,
            ),
          ),
          TextButton(
            onPressed: () {
              Get.snackbar(appName, 'Coming Soon');
            },
            child: const Text('See all'),
          ),
        ],
      ),
    );
  }
}

class CategoryBody extends StatelessWidget {
  const CategoryBody({
    super.key,
    required this.data,
  });

  final List data;

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 8),
        child: GridView.builder(
          itemCount: data.length,
          gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2,
            crossAxisSpacing: 16,
            mainAxisSpacing: 16,
            childAspectRatio: 0.75,
          ),
          itemBuilder: (context, index) {
            final program = data[index];
            final title = program['title'] ?? 'No Title';
            final description = program['description'] ?? '0 Courses';

            return MyCard(
              title: title,
              description: description,
              imagePath: imageSplash,
            );
          },
        ),
      ),
    );
  }
}

class BottomNavBar extends StatelessWidget {
  const BottomNavBar({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return BottomNavigationBar(
      items: const [
        BottomNavigationBarItem(
          icon: Icon(Icons.home),
          label: 'Home',
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.bookmark),
          label: 'Bookmark',
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.info),
          label: 'About',
        ),
      ],
    );
  }
}

database.json

[
  {
    "title": "Pemrograman",
    "description": "5 Course",
    "image": "assets/images/splash.png",
    "course": [
      {
        "title": "Mobile Learning",
        "description": "Mobile Learning",
        "image": "assets/images/splash.png",
        "chapter": [
          {
            "title": "chapter 1",
            "description": "",
            "image": "assets/images/splash.png",
            "status": "active",
            "type": "pdf",
            "link_youtube": "",
            "link_pdf": "https://www.qeios.com/read/GKPOHZ/pdf"
          },
          {
            "title": "chapter 2",
            "description": "",
            "image": "assets/images/splash.png",
            "status": "active",
            "type": "youtube",
            "link_youtube": "GSSzDIpyBuM",
            "link_pdf": ""
          }
        ]
      }
    ]
  },
  {
    "title": "Bahasa",
    "description": "2 Course",
    "image": "assets/images/splash.png",
    "course": [
      {
        "title": "Mobile Learning",
        "description": "Mobile Learning",
        "image": "assets/images/splash.png",
        "chapter": [
          {
            "title": "chapter 1",
            "description": "",
            "image": "assets/images/splash.png",
            "status": "active",
            "type": "pdf",
            "link_youtube": "",
            "link_pdf": "https://www.qeios.com/read/GKPOHZ/pdf"
          },
          {
            "title": "chapter 2",
            "description": "",
            "image": "assets/images/splash.png",
            "status": "active",
            "type": "youtube",
            "link_youtube": "GSSzDIpyBuM",
            "link_pdf": ""
          }
        ]
      }
    ]
  }
]

Mobile Learning

By Maulana Ilham

Mobile Learning

  • 348