Flutter Init, App, Home and Login Modules

Flutter Init, App, Home and Login Modules

[1] Dependencies configuration

file -> pubspec.yaml

name: workgroup_app
description: A flutter workgroup app.
publish_to: 'none'
version: 0.1.0

environment:
  sdk: '>=2.18.2 <3.0.0'

dependencies:
  flutter:
    sdk: flutter
  hive: ^2.2.3
  hive_flutter: ^1.1.0
  http: 0.13.0
  intl: ^0.17.0 # DateFormat

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^2.0.0

flutter:
  uses-material-design: true

[2] Project folder/file structure

lib/
├── main.dart
└── modules/
    ├── app.dart
    ├── home.dart
    ├── init.dart
    └── login.dart

[3] Main module

file-> lib/main.dart

import 'package:flutter/material.dart';
import 'modules/init.dart';
import 'modules/app.dart';

void main() async {
  await initializeApp();
  runApp(const App());
}

[4] Sub Modules

[4.1] Init

file-> lib/modules/init.dart

import 'package:hive_flutter/hive_flutter.dart';

Future<void> initializeApp() async {
  await _initHive();
  // Add any other initialization logic here
}

Future<void> _initHive() async {
  // Hive initialization logic
  await Hive.initFlutter();  // 
  await Hive.openBox("user");
}

[4.2] App

file-> lib/modules/app.dart

import 'package:flutter/material.dart';
import 'home.dart';
import 'workgroups.dart';

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Workgroup App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      debugShowCheckedModeBanner: false,
      initialRoute: '/',
      routes: {
        '/': (context) => const HomeScreen(),
        '/home': (context) => const HomeScreen(),
        '/workgroups': (context) => const WorkgroupsScreen(),        
      },
    );
  }
}

[4.3] Home

file-> lib/modules/home.dart

import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'login.dart';

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

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

class _HomePageState extends State<HomeScreen> {
  bool _isLoggedIn = false;
  final Box _boxUser = Hive.box("user");

  @override
  void initState() {
    super.initState();
    _checkUserToken();
  }

  Future<void> _checkUserToken() async {
    final userToken = await _boxUser.get('user_token');

    if (userToken == null) {
      // Redirect to login page
      Navigator.pushReplacement(
        context,
        MaterialPageRoute(builder: (_) => LoginScreen()),
      );
    } else {
      setState(() {
        _isLoggedIn = true;
      });
    }
  }

  Future<void> _logout() async {
    await _boxUser.delete('user_token');

    setState(() {
      _isLoggedIn = false;
    });

    // Redirect to login page
    Navigator.pushReplacement(
      context,
      MaterialPageRoute(builder: (_) => LoginScreen()),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
        actions: [
          if (_isLoggedIn)
            ElevatedButton(
              onPressed: _logout,
              style: ElevatedButton.styleFrom(
                backgroundColor: Colors.red,
                foregroundColor: Colors.white,
                padding:
                    const EdgeInsets.all(8.0),
              ),
              child: const Icon(Icons.logout),
            ),
        ],
      ),
      body: Center(
      child: ElevatedButton(
        onPressed: () {
          Navigator.pushNamed(context, '/workgroups');
        },
        style: ElevatedButton.styleFrom(
          padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), // Increase the button padding
          textStyle: const TextStyle(
            fontSize: 18, // Increase the font size
          ),
        ),
        child: const Text('Workgroups'),
      ),
      ),
    );
  }
}

Output (Screen) for App/Home:

[4.4] Login

file-> lib/modules/login.dart

import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:hive/hive.dart';

class LoginScreen extends StatefulWidget {
  @override
  _LoginScreenState createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen> {
  final _emailController = TextEditingController();
  final _passwordController = TextEditingController();
  final Box _boxUser = Hive.box("user");

Future<void> _login() async {
  final url = Uri.parse('https://demo.razzi.my/spotnet/public/api/login');
  final request = http.MultipartRequest('POST', url);

  request.fields['email'] = _emailController.text;
  request.fields['password'] = _passwordController.text;

  final response = await request.send();
  final responseBody = await response.stream.bytesToString();

  if (response.statusCode == 200) {
    final data = jsonDecode(responseBody);
    final email = data['email'];
    final userToken = data['user_token'];

    // Update the 'boxUser' box
    await _boxUser.put('email', email);
    await _boxUser.put('user_token', userToken);

    // Handle the successful login response
    print(data);
    // Navigate to the home page
    Navigator.pushReplacementNamed(context, '/home');

  } else {
    // Handle the login error
    print('Login failed: $responseBody');
  }
}

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Login'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextField(
              controller: _emailController,
              decoration: InputDecoration(
                labelText: 'Email',
              ),
            ),
            SizedBox(height: 16.0),
            TextField(
              controller: _passwordController,
              obscureText: true,
              decoration: InputDecoration(
                labelText: 'Password',
              ),
            ),
            SizedBox(height: 16.0),
            ElevatedButton(
              onPressed: _login,
              child: Text('Login'),
            ),
          ],
        ),
      ),
    );
  }
}

Output (Screen) for Login: