Tugas praktikum 1 dan 2

1. MyApp

  • Ini kayak base app lo.

  • Set theme warna teal (ijo-ijo fresh ๐ŸŒฟ), font Roboto, AppBar rapih, sama style buat bottom nav.

  • debugShowCheckedModeBanner: false biar gak ada tulisan "DEBUG" yang jelek itu.


2. HomePage (Main Menu)

  • Punya variable _selectedIndex → buat tau lo lagi di tab mana (0 = dashboard, 1 = profil, 2 = setting).

  • BottomNavigationBar = menu bawah ala aplikasi kekinian.

  • Jadi kalo lo klik tab, _onItemTapped() bakal update _selectedIndex → dan otomatis body ganti sesuai page.


3. ResponsiveLayoutPage (Dashboard)

  • Gunanya buat tampilan utama.

  • Pake LayoutBuilder → ngecek layar user gede apa kecil.

    • Layar lebar (>600px) → layout lebih lega, ada row/wrap.

    • Layar kecil (HP) → layout column biar pas di layar.

  • Isinya:
    ✅ Judul + deskripsi
    ✅ Banner image (random dari picsum.photos)
    ✅ Feature cards (performa, keamanan, penyimpanan, pengaturan)
    ✅ Tombol ke halaman kedua → transisi animasi slide + fade ๐Ÿ”ฅ


4. FeatureCard

  • Kartu kecil berisi icon + title + subtitle.

  • Kayak shortcut fitur di dashboard (contohnya: monitor aplikasi, proteksi data).

  • Biar gak flat, dikasih card + elevation.


5. ProfilePage

  • Isinya data profil user.

  • Ada foto profil (CircleAvatar), nama, email.

  • Bawahnya ada 2 kartu:

    • Card 1 → Info pribadi, keamanan akun, metode pembayaran, bahasa.

    • Card 2 → Bantuan, feedback, tentang aplikasi.

  • Pake LayoutBuilder → kalau di tablet/PC, 2 card sejajar; kalo HP ditumpuk ke bawah.


6. SettingPage

  • Menu pengaturan ala app beneran.

  • Ada 3 card:

    1. Notifikasi, privasi, penyimpanan

    2. Bahasa, tema gelap (ada switch toggle), tampilan

    3. Bantuan & tentang aplikasi

  • Lagi-lagi responsif → row di layar gede, column di layar kecil.


7. SecondPage

  • Halaman kedua yang bisa lo buka dari dashboard.

  • Desainnya fancy: ada gambar random, judul, deskripsi, sama tombol buat balik.

  • Background warna soft green biar beda feel sama dashboard.

  • Responsif juga → ukuran teks & gambar adjust sesuai layar.


๐ŸŽฏ Intinya

Kode ini tuh udah template aplikasi modern:

  • UI clean & aesthetic (teal vibes ๐ŸŒฟ).

  • Responsive design → jalan di HP, tablet, bahkan desktop.

  • Ada navigation bar bawah kayak app real.

  • Ada animasi transisi biar gak kaku.

  • Ada profil + setting page biar berasa legit.


⚡ Singkatnya:
Lo bikin aplikasi Flutter yang udah mirip app beneran, cocok banget jadi dasar buat project lebih gede.

 import 'package:flutter/material.dart';


void main() => runApp(const MyApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Responsive App',
      theme: ThemeData(
        primarySwatch: Colors.teal,
        fontFamily: 'Roboto',
        appBarTheme: const AppBarTheme(
          backgroundColor: Colors.teal,
          elevation: 0,
          centerTitle: true,
          iconTheme: IconThemeData(color: Colors.white),
        ),
        bottomNavigationBarTheme: const BottomNavigationBarThemeData(
          backgroundColor: Colors.white,
          selectedItemColor: Colors.teal,
          unselectedItemColor: Colors.grey,
          elevation: 8,
        ),
      ),
      home: const HomePage(),
    );
  }
}

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _selectedIndex = 0;

  final List<Widget> _pages = [
    const ResponsiveLayoutPage(),
    const ProfilePage(),
    const SettingPage(),
  ];

  void _onItemTapped(int index) {
    setState(() => _selectedIndex = index);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey[50],
      appBar: AppBar(
        title: Text(
          _selectedIndex == 0
              ? "Dashboard"
              : _selectedIndex == 1
                  ? "Profil Saya"
                  : "Pengaturan",
          style: const TextStyle(
            fontWeight: FontWeight.w600,
            fontSize: 20,
          ),
        ),
        actions: [
          IconButton(
            icon: const Icon(Icons.notifications_none),
            onPressed: () {},
          ),
          const SizedBox(width: 8),
        ],
      ),
      body: _pages[_selectedIndex],
      bottomNavigationBar: Container(
        decoration: BoxDecoration(
          boxShadow: [
            BoxShadow(
              color: Colors.grey.withOpacity(0.3),
              blurRadius: 10,
              offset: const Offset(0, -5),
            ),
          ],
        ),
        child: BottomNavigationBar(
          items: const [
            BottomNavigationBarItem(
              icon: Icon(Icons.dashboard_rounded),
              label: "Dashboard",
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.person_rounded),
              label: "Profil",
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.settings_rounded),
              label: "Setting",
            ),
          ],
          currentIndex: _selectedIndex,
          onTap: _onItemTapped,
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth > 600) {
          return _buildWideLayout(context);
        } else {
          return _buildNarrowLayout(context);
        }
      },
    );
  }

  Widget _buildWideLayout(BuildContext context) {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(24),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Text(
            "Konten Utama",
            style: TextStyle(
              fontSize: 24,
              fontWeight: FontWeight.bold,
              color: Colors.teal,
            ),
          ),
          const SizedBox(height: 16),
          const Text(
            "Selamat datang di aplikasi Flutter! Jelajahi berbagai fitur yang tersedia.",
            style: TextStyle(fontSize: 16, color: Colors.grey),
          ),
          const SizedBox(height: 30),
          Center(
            child: Card(
              elevation: 5,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(16),
              ),
              child: ClipRRect(
                borderRadius: BorderRadius.circular(16),
                child: Image.network(
                  "https://picsum.photos/600/300",
                  width: 500,
                  height: 250,
                  fit: BoxFit.cover,
                  errorBuilder: (context, error, stackTrace) => Container(
                    width: 500,
                    height: 250,
                    color: Colors.grey[300],
                    child: const Icon(Icons.error),
                  ),
                ),
              ),
            ),
          ),
          const SizedBox(height: 30),
          // Responsive feature cards
          LayoutBuilder(
            builder: (context, constraints) {
              if (constraints.maxWidth > 800) {
                return Row(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: _buildFeatureCards(),
                );
              } else {
                return Wrap(
                  spacing: 16,
                  runSpacing: 16,
                  alignment: WrapAlignment.center,
                  children: _buildFeatureCards(),
                );
              }
            },
          ),
          const SizedBox(height: 30),
          // Tombol untuk halaman kedua
          Center(
            child: ElevatedButton.icon(
              style: ElevatedButton.styleFrom(
                backgroundColor: Colors.teal,
                foregroundColor: Colors.white,
                padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(12),
                ),
                elevation: 2,
              ),
              onPressed: () {
                Navigator.push(
                  context,
                  PageRouteBuilder(
                    pageBuilder: (context, animation, secondaryAnimation) =>
                        const SecondPage(),
                    transitionsBuilder:
                        (context, animation, secondaryAnimation, child) {
                      const begin = Offset(1.0, 0.0);
                      const end = Offset.zero;
                      const curve = Curves.easeInOut;

                      var tween = Tween(begin: begin, end: end)
                          .chain(CurveTween(curve: curve));
                      var fadeTween = Tween<double>(begin: 0.0, end: 1.0);

                      return SlideTransition(
                        position: animation.drive(tween),
                        child: FadeTransition(
                          opacity: animation.drive(fadeTween),
                          child: child,
                        ),
                      );
                    },
                  ),
                );
              },
              icon: const Icon(Icons.arrow_forward_rounded),
              label: const Text("Pergi ke Halaman Kedua"),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildNarrowLayout(BuildContext context) {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Text(
            "Konten Utama",
            style: TextStyle(
              fontSize: 22,
              fontWeight: FontWeight.bold,
              color: Colors.teal,
            ),
          ),
          const SizedBox(height: 12),
          const Text(
            "Selamat datang di aplikasi Flutter! Jelajahi berbagai fitur yang tersedia.",
            style: TextStyle(fontSize: 15, color: Colors.grey),
          ),
          const SizedBox(height: 20),
          Card(
            elevation: 5,
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(16),
            ),
            child: ClipRRect(
              borderRadius: BorderRadius.circular(16),
              child: Image.network(
                "https://picsum.photos/400/200",
                width: double.infinity,
                height: 180,
                fit: BoxFit.cover,
                errorBuilder: (context, error, stackTrace) => Container(
                  height: 180,
                  color: Colors.grey[300],
                  child: const Icon(Icons.error),
                ),
              ),
            ),
          ),
          const SizedBox(height: 20),
          const Text(
            "Fitur Utama",
            style: TextStyle(
              fontSize: 18,
              fontWeight: FontWeight.w600,
            ),
          ),
          const SizedBox(height: 12),
          // Responsive feature cards for mobile
          LayoutBuilder(
            builder: (context, constraints) {
              if (constraints.maxWidth > 400) {
                return Wrap(
                  spacing: 16,
                  runSpacing: 16,
                  alignment: WrapAlignment.center,
                  children: _buildFeatureCards(),
                );
              } else {
                return Column(
                  children: _buildFeatureCards()
                      .map((card) => Padding(
                            padding: const EdgeInsets.only(bottom: 12),
                            child: card,
                          ))
                      .toList(),
                );
              }
            },
          ),
          const SizedBox(height: 20),
          // Tombol untuk halaman kedua di mobile
          Center(
            child: ElevatedButton.icon(
              style: ElevatedButton.styleFrom(
                backgroundColor: Colors.teal,
                foregroundColor: Colors.white,
                padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(12),
                ),
                elevation: 2,
              ),
              onPressed: () {
                Navigator.push(
                  context,
                  PageRouteBuilder(
                    pageBuilder: (context, animation, secondaryAnimation) =>
                        const SecondPage(),
                    transitionsBuilder:
                        (context, animation, secondaryAnimation, child) {
                      const begin = Offset(1.0, 0.0);
                      const end = Offset.zero;
                      const curve = Curves.easeInOut;

                      var tween = Tween(begin: begin, end: end)
                          .chain(CurveTween(curve: curve));
                      var fadeTween = Tween<double>(begin: 0.0, end: 1.0);

                      return SlideTransition(
                        position: animation.drive(tween),
                        child: FadeTransition(
                          opacity: animation.drive(fadeTween),
                          child: child,
                        ),
                      );
                    },
                  ),
                );
              },
              icon: const Icon(Icons.arrow_forward_rounded),
              label: const Text("Pergi ke Halaman Kedua"),
            ),
          ),
        ],
      ),
    );
  }

  List<Widget> _buildFeatureCards() {
    return [
      FeatureCard(
        icon: Icons.speed,
        title: "Performa",
        subtitle: "Monitor aplikasi",
      ),
      FeatureCard(
        icon: Icons.security,
        title: "Keamanan",
        subtitle: "Proteksi data",
      ),
      FeatureCard(
        icon: Icons.storage,
        title: "Penyimpanan",
        subtitle: "Kelola file",
      ),
      FeatureCard(
        icon: Icons.settings,
        title: "Pengaturan",
        subtitle: "Kustomisasi",
      ),
    ];
  }
}

class FeatureCard extends StatelessWidget {
  final IconData icon;
  final String title;
  final String subtitle;

  const FeatureCard({
    super.key,
    required this.icon,
    required this.title,
    required this.subtitle,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      elevation: 3,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(12),
      ),
      child: Container(
        width: 120,
        padding: const EdgeInsets.all(12),
        child: Column(
          children: [
            Icon(icon, size: 32, color: Colors.teal),
            const SizedBox(height: 8),
            Text(
              title,
              style: const TextStyle(
                fontWeight: FontWeight.bold,
                fontSize: 14,
              ),
              textAlign: TextAlign.center,
            ),
            Text(
              subtitle,
              style: const TextStyle(
                fontSize: 12,
                color: Colors.grey,
              ),
              textAlign: TextAlign.center,
            ),
          ],
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final bool isWide = constraints.maxWidth > 600;
       
        return SingleChildScrollView(
          padding: EdgeInsets.all(isWide ? 40 : 20),
          child: Column(
            children: [
              const SizedBox(height: 20),
              const CircleAvatar(
                radius: 60,
                backgroundImage: NetworkImage("https://picsum.photos/200"),
              ),
              const SizedBox(height: 20),
              const Text(
                "Reihan Novalendra H",
                style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 8),
              const Text(
                "reihan.novalendra@example.com",
                style: TextStyle(fontSize: 16, color: Colors.grey),
              ),
              const SizedBox(height: 30),
             
              // Responsive profile cards
              if (isWide)
                Row(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Expanded(
                      child: _buildProfileCard1(),
                    ),
                    const SizedBox(width: 20),
                    Expanded(
                      child: _buildProfileCard2(),
                    ),
                  ],
                )
              else
                Column(
                  children: [
                    _buildProfileCard1(),
                    const SizedBox(height: 20),
                    _buildProfileCard2(),
                  ],
                ),
            ],
          ),
        );
      },
    );
  }

  Widget _buildProfileCard1() {
    return Card(
      elevation: 3,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16),
      ),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            _ProfileItem(Icons.person, "Informasi Pribadi"),
            _ProfileItem(Icons.lock, "Keamanan Akun"),
            _ProfileItem(Icons.payment, "Metode Pembayaran"),
            _ProfileItem(Icons.language, "Bahasa"),
          ],
        ),
      ),
    );
  }

  Widget _buildProfileCard2() {
    return Card(
      elevation: 3,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16),
      ),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            _ProfileItem(Icons.help, "Pusat Bantuan"),
            _ProfileItem(Icons.feedback, "Berikan Masukan"),
            _ProfileItem(Icons.info, "Tentang Aplikasi"),
          ],
        ),
      ),
    );
  }
}

class _ProfileItem extends StatelessWidget {
  final IconData icon;
  final String title;

  const _ProfileItem(this.icon, this.title);

  @override
  Widget build(BuildContext context) {
    return ListTile(
      leading: Icon(icon, color: Colors.teal),
      title: Text(title),
      trailing: const Icon(Icons.arrow_forward_ios, size: 16),
      onTap: () {},
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final bool isWide = constraints.maxWidth > 600;
       
        return SingleChildScrollView(
          padding: EdgeInsets.all(isWide ? 40 : 20),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              const Text(
                "Pengaturan",
                style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 20),
             
              // Responsive settings layout
              if (isWide)
                Row(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Expanded(child: _buildSettingsCard1()),
                    const SizedBox(width: 20),
                    Expanded(child: _buildSettingsCard2()),
                    const SizedBox(width: 20),
                    Expanded(child: _buildSettingsCard3()),
                  ],
                )
              else
                Column(
                  children: [
                    _buildSettingsCard1(),
                    const SizedBox(height: 20),
                    _buildSettingsCard2(),
                    const SizedBox(height: 20),
                    _buildSettingsCard3(),
                  ],
                ),
            ],
          ),
        );
      },
    );
  }

  Widget _buildSettingsCard1() {
    return Card(
      elevation: 3,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16),
      ),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            _SettingItem(
              Icons.notifications,
              "Notifikasi",
              "Kelola notifikasi aplikasi",
            ),
            _SettingItem(
              Icons.privacy_tip,
              "Privasi",
              "Kelola data dan privasi",
            ),
            _SettingItem(
              Icons.storage,
              "Penyimpanan",
              "Penggunaan penyimpanan",
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildSettingsCard2() {
    return Card(
      elevation: 3,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16),
      ),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            _SettingItem(
              Icons.language,
              "Bahasa",
              "Pilih bahasa aplikasi",
            ),
            _SettingItem(
              Icons.dark_mode,
              "Tema Gelap",
              "Aktifkan tema gelap",
              isSwitch: true,
            ),
            _SettingItem(
              Icons.wallpaper,
              "Tampilan",
              "Kustomisasi tampilan",
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildSettingsCard3() {
    return Card(
      elevation: 3,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16),
      ),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            _SettingItem(
              Icons.help,
              "Bantuan & Dukungan",
              "Dapatkan bantuan",
            ),
            _SettingItem(
              Icons.info,
              "Tentang Aplikasi",
              "Versi 1.0.0",
            ),
          ],
        ),
      ),
    );
  }
}

class _SettingItem extends StatelessWidget {
  final IconData icon;
  final String title;
  final String subtitle;
  final bool isSwitch;

  const _SettingItem(
    this.icon,
    this.title,
    this.subtitle, {
    this.isSwitch = false,
  });

  @override
  Widget build(BuildContext context) {
    return ListTile(
      leading: Icon(icon, color: Colors.teal),
      title: Text(title),
      subtitle: Text(subtitle),
      trailing: isSwitch
          ? Switch(
              value: true,
              onChanged: (value) {},
              activeColor: Colors.teal,
            )
          : const Icon(Icons.arrow_forward_ios, size: 16),
      onTap: () {},
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.green[50],
      appBar: AppBar(
        title: const Text("Halaman Kedua"),
      ),
      body: LayoutBuilder(
        builder: (context, constraints) {
          final bool isWide = constraints.maxWidth > 600;
         
          return Center(
            child: SingleChildScrollView(
              padding: EdgeInsets.all(isWide ? 40 : 20),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Card(
                    elevation: 8,
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(20),
                    ),
                    child: ClipRRect(
                      borderRadius: BorderRadius.circular(20),
                      child: Image.network(
                        "https://picsum.photos/250/250",
                        width: isWide ? 300 : 200,
                        height: isWide ? 300 : 200,
                        fit: BoxFit.cover,
                        errorBuilder: (context, error, stackTrace) =>
                            Container(
                          width: isWide ? 300 : 200,
                          height: isWide ? 300 : 200,
                          color: Colors.grey[300],
                          child: const Icon(Icons.error),
                        ),
                      ),
                    ),
                  ),
                  const SizedBox(height: 30),
                  Text(
                    "Halaman Kedua",
                    style: TextStyle(
                      fontSize: isWide ? 28 : 24,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 10),
                  Padding(
                    padding: EdgeInsets.symmetric(
                        horizontal: isWide ? 100 : 40),
                    child: const Text(
                      "Ini adalah halaman kedua aplikasi dengan desain yang lebih menarik dan modern.",
                      textAlign: TextAlign.center,
                      style: TextStyle(fontSize: 16, color: Colors.grey),
                    ),
                  ),
                  const SizedBox(height: 30),
                  ElevatedButton.icon(
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.teal,
                      foregroundColor: Colors.white,
                      padding: const EdgeInsets.symmetric(
                          horizontal: 24, vertical: 14),
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(12),
                      ),
                      elevation: 3,
                    ),
                    onPressed: () => Navigator.pop(context),
                    icon: const Icon(Icons.arrow_back),
                    label: const Text("Kembali ke Halaman Utama"),
                  ),
                ],
              ),
            ),
          );
        },
      ),
    );
  }
}

link aplikasi nya: https://zt1kk063vt1kl.zapp.page/#/

Komentar

Postingan populer dari blog ini

๐Ÿš€ Bikin Aplikasi Jadwal Penerbangan Offline Keren dengan Flutter Web (Pakai zapp.run) ✈️

Jenis jenis sistem operasi mobile