Skip to content

Instantly share code, notes, and snippets.

@imamnurhy
Last active January 10, 2023 05:26
Show Gist options
  • Select an option

  • Save imamnurhy/2a025472de65b0f6205711fbb3cccf6c to your computer and use it in GitHub Desktop.

Select an option

Save imamnurhy/2a025472de65b0f6205711fbb3cccf6c to your computer and use it in GitHub Desktop.
Flutter

Futter Clean Architecture

Dibuat untuk mempermudah dalam pengembangan project flutter dengan flutter clean architecture kita dapat tidak akan bingung lagi ketika aplikasi kita mengalami error karena akan sangat mudah dalam mengatasinya karena kita di haruskan untuk memisahkan setiap class, fungsi dan lain sebagainya.


Struktur sederhada project pada flutter

|-lib
|--app
|---bloc
|---models
|---providers
|---utilities
|--pages
|---widgets
|--routes

Sedikit penjelasana pada struktur folder di atas :

  1. Lib

    Folder ini merupakan inti dari semua source code program pada aplikasi

  2. App

    Folder app ini di gunakan untuk menyimpan beberapa source yang bertugas sebagai logic, model, keperluan, dan koneksi ke Endpoint atau RestApi, diantaranya sebagai berikut :

    • Bloc design pattern yang membantu untuk memisahkan presentation dengan business logic. Sehingga file kita nantinya jadi lebih tersusun sesuai dengan fungsinya masing-masing.
    • Models digunakan untuk menyimpan response dari endpoint yang kita panggil dan biasanya menggunakan json
    • Providers bertugas untuk mengambil data dari endpoint yang telah ditentukan
    • Utilities saya gunakan untuk keperluan-keperluan seperti membuat class, function, dan lain sebagainya
  3. Pages

    Nah kalo ini sudah pasti kita tau folder ini digunakan menyimpan semua tampilan UI(Users Interface) aplikasi seperti halaman splash, login, register, dan banyak lagi.

  4. Routes

    Digunakn untuk meyimpan semua routing page di aplikasi

class DateTimeFormat {
static format({String format, DateTime dateTime}) {
if (dateTime != null) {
return DateFormat(format).format(dateTime);
} else {
return '-';
}
}
}
DropdownSearch<dynamic>(
mode: Mode.MENU,
items: countries.map((e) => e['name']).toList(),
showSearchBox: true,
onChanged: (value) {
var selected = countries.firstWhere((e) => e['name'] == value);
print(selected['id']);
},
selectedItem: "Canada",
)
extension StringExtension on String {
String capitalize() {
return "${this[0].toUpperCase()}${this.substring(1)}";
}
String firstCharacter() {
return "${this[0].substring(0, 1).toUpperCase()}";
}
}
import 'package:flutter/material.dart' show Color;
class HexColors extends Color {
static color(String hexColor, String opacity) {
hexColor = hexColor == '' ? '00BCD4' : hexColor;
if (hexColor.length == 6) {
hexColor = opacity + hexColor;
}
if (hexColor.length == 8) {
return int.parse("0x$hexColor");
}
}
/// With Opacity
/// [
/// 100:'FF', 95:'F2',90:'E6', 85:'D9', 80:'CC', 75:'BF', 70:'B3', 65:'A6', 60:'99', 55:'8C', 50:'80', 45:'73', 40:'66', 35:'59', 30:'4D', 25:'40', 20:'33', 15:'26', 10:'1A', 5:'0D', 0:'00'
/// ]
HexColors(final String hexColor, {String opacity}) : super(color(hexColor, opacity));
}
import 'dart:convert';
import 'package:http_network/http_network.dart';
class HttpHandle {
HttpNetwork _http = HttpNetwork();
Future<Response> get(
String url, {
Map<String, String> headers = const {},
}) async {
try {
final response = await _http.get(
url,
headers: headers,
);
return response;
} on ClientErrorException catch (e) {
Map error = jsonDecode(e.body);
if (error['message'] == null) {
throw error['status'];
} else {
throw error['message'];
}
} on ServerErrorException catch (e) {
Map error = jsonDecode(e.body);
if (error['message'] == null) {
throw error['status'];
} else {
throw error['message'];
}
} catch (e) {
rethrow;
}
}
Future<Response> post(
String url, {
Map<String, String> headers = const {},
dynamic body = const {},
Map<String, String> files = const {},
}) async {
try {
final response = await _http.post(
url,
headers: headers,
body: body,
files: files,
);
return response;
} on ClientErrorException catch (e) {
Map error = jsonDecode(e.body);
if (error['message'] == null) {
throw error['status'];
} else {
throw error['message'];
}
} on ServerErrorException catch (e) {
Map error = jsonDecode(e.body);
if (error['message'] == null) {
throw error['status'];
} else {
throw error['message'];
}
} catch (e) {
rethrow;
}
}
}
name: name application
description: A new Flutter project.
version: 1.0.0+1
environment:
sdk: ">=2.10.4 <3.0.0"
dependencies:
flutter:
sdk: flutter
# core packages
bloc: ^6.1.0
flutter_bloc: ^6.1.1
package_info: ^0.4.3+2
path_provider: ^1.6.24
shared_preferences: ^0.5.12+2
intl: ^0.16.0
http: ^0.12.2
rxdart: ^0.23.1
dev_dependencies:
flutter_test:
sdk: flutter
flutter_icons:
android: "launcher_icon"
ios: true
image_path: path to icon
flutter:
uses-material-design: true
assets:
- assets/
fonts:
- family: font family
fonts:
- asset: path to font
import 'package:flutter/material.dart';
import './Utilities/style.dart';
class SplashPage extends StatefulWidget {
const SplashPage({Key? key}) : super(key: key);
@override
_SplashPageState createState() => _SplashPageState();
}
class _SplashPageState extends State<SplashPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: gradientColorPrimary,
),
),
child: Stack(
fit: StackFit.expand,
children: <Widget>[
// Widget : Instansi
Column(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(bottom: 50.0),
child: DefaultTextStyle(
style: const TextStyle(fontSize: 16.0, color: Colors.white),
child: Column(
children: const <Widget>[
Text("From"),
Text('Diskominfo Kota Tangerang Selatan'),
],
),
),
)
],
),
// Widget : Icon
Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Image.asset(
'assets/woman.png',
height: 180,
filterQuality: FilterQuality.high,
),
const Text(
'Smart Kencana',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 30.0,
),
),
],
),
],
),
),
);
}
}
import 'package:shared_preferences/shared_preferences.dart';
class Storage {
// Set storage
static void setString({String key, String value}) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString(key, value);
}
static void setBool({String key, bool value}) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool(key, value);
}
// Get storage
static Future<String> getString({String key}) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString(key) ?? '';
}
static Future<bool> getBool({String key}) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getBool(key) ?? false;
}
}

Widget for flutter application

Http Network Handle

import 'dart:convert';

import 'package:http_network/http_network.dart';

class HttpHandle {
  HttpNetwork _http = HttpNetwork(logs: true);

  Future<Response> get(
    String url, {
    Map<String, String> headers = const {},
  }) async {
    try {
      final response = await _http.get(
        url,
        headers: headers,
      );
      return response;
    } on ClientErrorException catch (e) {
      throw _handleClientError(e);
    } on ServerErrorException catch (e) {
      throw _handleServerError(e);
    } catch (e) {
      rethrow;
    }
  }

  Future<Response> post(
    String url, {
    Map<String, String> headers = const {},
    dynamic body = const {},
    Map<String, String> files = const {},
  }) async {
    try {
      final response = await _http.post(
        url,
        headers: headers,
        body: body,
        files: files,
      );
      return response;
    } on ClientErrorException catch (e) {
      throw _handleClientError(e);
    } on ServerErrorException catch (e) {
      throw _handleServerError(e);
    } catch (e) {
      rethrow;
    }
  }

  //=== Handle Exception Error ===//

  _handleClientError(ClientErrorException exception) {
    Map body = jsonDecode(exception.body);
    if (body.isNotEmpty) {
      if (body.containsKey('message')) {
        throw body['message'];
      } else {
        throw exception.message;
      }
    } else {
      throw exception.message;
    }
  }

  _handleServerError(ServerErrorException exception) {
    Map body = jsonDecode(exception.body);
    if (body.isNotEmpty) {
      if (body.containsKey('message')) {
        throw body['message'];
      } else {
        throw exception.message;
      }
    } else {
      throw exception.message;
    }
  }
}

Loading Dialog

showDialog(
  context: context,
  builder: (_) => Center(
    child: Card(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(5.0),
      ),
      child: Padding(
        padding: const EdgeInsets.all(10.0),
        child: CircularProgressIndicator(),
      ),
    ),
  ),
);

Dialog success

showDialog(
  context: context,
  builder: (_) => AlertDialog(
    title: Text('Berhasil'),
    content: Text('Data berhasil Tersimpan'),
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(10.0),
    ),
    actions: [
      TextButton(
        onPressed: () => Navigator.pop(context),
        child: Text('Kembali'),
      )
    ],
  ),
);

Dialog Error

showDialog(
  context: context,
  builder: (_) => AlertDialog(
    title: Text('Kesalahan'),
    content: Text('Data gagal disimpan'),
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(10.0),
    ),
    actions: [
      TextButton(
        onPressed: () => Navigator.pop(context),
        child: Text('Kembali'),
      )
    ],
  ),
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment