feat: base project

This commit is contained in:
efrilm
2025-08-12 15:18:38 +07:00
parent 2df3bb118a
commit cb5250459e
102 changed files with 3453 additions and 333 deletions
+238
View File
@@ -0,0 +1,238 @@
import 'package:awesome_dio_interceptor/awesome_dio_interceptor.dart';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:injectable/injectable.dart';
import '../../env.dart';
import 'api_failure.dart';
import 'errors/bad_network_error.dart';
import 'errors/bad_request_error.dart';
import 'errors/connection_timeout_error.dart';
import 'errors/internal_server_error.dart';
import 'errors/not_found_error.dart';
import 'errors/unauthorized_error.dart';
import 'interceptors/bad_network_interceptor.dart';
import 'interceptors/bad_request_interceptor.dart';
import 'interceptors/connection_timeout_interceptor.dart';
import 'interceptors/internal_server_interceptor.dart';
import 'interceptors/not_found_interceptor.dart';
import 'interceptors/unauthorized_interceptor.dart';
@lazySingleton
class ApiClient {
final Dio _dio;
final Env _env;
ApiClient(this._dio, this._env) {
_dio.options.baseUrl = _env.baseUrl;
_dio.options.connectTimeout = const Duration(seconds: 20);
_dio.interceptors.add(BadNetworkErrorInterceptor());
_dio.interceptors.add(BadRequestErrorInterceptor());
_dio.interceptors.add(InternalServerErrorInterceptor());
_dio.interceptors.add(NotFoundErrorInterceptor());
_dio.interceptors.add(UnauthorizedInterceptor());
_dio.interceptors.add(ConnectionTimeoutErrorInterceptor());
if (kDebugMode) {
_dio.interceptors.add(
AwesomeDioInterceptor(
logResponseHeaders: false,
logRequestTimeout: false,
logRequestHeaders: true,
),
);
}
}
Future<Response> post(
String path, {
dynamic data,
Map<String, dynamic>? headers,
Map<String, dynamic>? params,
bool followRedirects = true,
bool Function(int?)? validateStatus,
String? contentType,
}) async {
try {
return await _dio.post(
path,
data: data,
options: Options(
headers: headers,
followRedirects: followRedirects,
validateStatus: validateStatus,
contentType: contentType,
),
queryParameters: params,
);
} on UnauthorizedError catch (e) {
throw ApiFailure.unauthorized(e.messageError);
} on InternalServerError {
throw const ApiFailure.internalServerError();
} on BadNetworkError {
throw const ApiFailure.connectionError();
} on BadRequestError catch (e) {
throw ApiFailure.badRequest(e.messageError);
} on NotFoundError catch (e) {
throw ApiFailure.notFound(e.messageError);
} on ConnectionTimeoutError {
throw const ApiFailure.connectionTimeout();
} on DioException catch (e) {
var errorMessage =
e.response?.data['message'] ?? e.response?.statusMessage ?? e.error;
if (errorMessage.toString().contains('Connection reset')) {
errorMessage = 'Connection reset';
}
throw ApiFailure.serverError(
statusCode: e.response?.statusCode ?? 0,
errorMessage: errorMessage.toString(),
);
} catch (e, s) {
throw ApiFailure.unexpectedError(errorMessage: e, stackTrace: s);
}
}
Future<Response> get(
String path, {
Map<String, dynamic>? headers,
Map<String, dynamic>? params,
bool followRedirects = true,
bool Function(int?)? validateStatus,
String? contentType,
}) async {
try {
return await _dio.get(
path,
options: Options(
headers: headers,
followRedirects: followRedirects,
validateStatus: validateStatus,
contentType: contentType,
),
queryParameters: params,
);
} on UnauthorizedError catch (e) {
throw ApiFailure.unauthorized(e.messageError);
} on InternalServerError {
throw const ApiFailure.internalServerError();
} on BadNetworkError {
throw const ApiFailure.connectionError();
} on BadRequestError catch (e) {
throw ApiFailure.badRequest(e.messageError);
} on NotFoundError catch (e) {
throw ApiFailure.notFound(e.messageError);
} on ConnectionTimeoutError {
throw const ApiFailure.connectionTimeout();
} on DioException catch (e) {
var errorMessage =
e.response?.data['message'] ?? e.response?.statusMessage ?? e.error;
if (errorMessage.toString().contains('Connection reset')) {
errorMessage = 'Connection reset';
}
throw ApiFailure.serverError(
statusCode: e.response?.statusCode ?? 0,
errorMessage: errorMessage.toString(),
);
} catch (e, s) {
throw ApiFailure.unexpectedError(errorMessage: e, stackTrace: s);
}
}
Future<Response> put(
String path, {
dynamic data,
Map<String, dynamic>? headers,
Map<String, dynamic>? params,
bool followRedirects = true,
bool Function(int?)? validateStatus,
String? contentType,
void Function(int count, int total)? onSendProgress,
}) async {
try {
return await _dio.put(
path,
data: data,
options: Options(
headers: headers,
followRedirects: followRedirects,
validateStatus: validateStatus,
contentType: contentType,
),
queryParameters: params,
onSendProgress: onSendProgress,
);
} on UnauthorizedError catch (e) {
throw ApiFailure.unauthorized(e.messageError);
} on InternalServerError {
throw const ApiFailure.internalServerError();
} on BadNetworkError {
throw const ApiFailure.connectionError();
} on BadRequestError catch (e) {
throw ApiFailure.badRequest(e.messageError);
} on NotFoundError catch (e) {
throw ApiFailure.notFound(e.messageError);
} on ConnectionTimeoutError {
throw const ApiFailure.connectionTimeout();
} on DioException catch (e) {
var errorMessage =
e.response?.data['message'] ?? e.response?.statusMessage ?? e.error;
if (errorMessage.toString().contains('Connection reset')) {
errorMessage = 'Connection reset';
}
throw ApiFailure.serverError(
statusCode: e.response?.statusCode ?? 0,
errorMessage: errorMessage.toString(),
);
} catch (e, s) {
throw ApiFailure.unexpectedError(errorMessage: e, stackTrace: s);
}
}
Future<Response> delete(
String path, {
dynamic data,
Map<String, dynamic>? headers,
Map<String, dynamic>? params,
bool followRedirects = true,
bool Function(int?)? validateStatus,
String? contentType,
}) async {
try {
return await _dio.delete(
path,
data: data,
options: Options(
headers: headers,
followRedirects: followRedirects,
validateStatus: validateStatus,
contentType: contentType,
),
queryParameters: params,
);
} on UnauthorizedError catch (e) {
throw ApiFailure.unauthorized(e.messageError);
} on InternalServerError {
throw const ApiFailure.internalServerError();
} on BadNetworkError {
throw const ApiFailure.connectionError();
} on BadRequestError catch (e) {
throw ApiFailure.badRequest(e.messageError);
} on NotFoundError catch (e) {
throw ApiFailure.notFound(e.messageError);
} on DioException catch (e) {
throw ApiFailure.serverError(
statusCode: e.response?.statusCode ?? 0,
errorMessage:
e.response?.data['message'] ?? e.response?.statusMessage ?? e.error,
);
} catch (e, s) {
throw ApiFailure.unexpectedError(errorMessage: e, stackTrace: s);
}
}
}
+58
View File
@@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'api_failure.freezed.dart';
@freezed
sealed class ApiFailure with _$ApiFailure {
const ApiFailure._();
const factory ApiFailure.serverError({
required int statusCode,
required Object errorMessage,
}) = _ServerError;
const factory ApiFailure.unexpectedError({
required Object errorMessage,
required StackTrace stackTrace,
}) = _UnexpectedError;
const factory ApiFailure.connectionError() = _ConnectionError;
const factory ApiFailure.internalServerError() = _InternalServerError;
const factory ApiFailure.unauthorized(String? message) = _Unauthorized;
const factory ApiFailure.badRequest(String? message) = _BadRequest;
const factory ApiFailure.notFound(String? message) = _NotFound;
const factory ApiFailure.connectionTimeout() = _ConnectionTimeout;
String toStringFormatted(
BuildContext context, {
String? unauthorizedMessage,
}) {
return switch (this) {
_ServerError(:final statusCode, :final errorMessage) =>
'There is a problem with the server. Status code: $statusCode Error: $errorMessage',
_UnexpectedError() => 'An error occurred. Please try again later.',
_ConnectionError() => 'No Internet',
_InternalServerError() =>
'The server is experiencing problems. Please try again later.',
_Unauthorized(:final message) =>
message ?? unauthorizedMessage ?? 'Session has expired.',
_BadRequest(:final message) =>
message ?? 'There is an incorrect entry. Please check again',
_NotFound(:final message) => message ?? 'Not Found',
_ConnectionTimeout() => 'Connection Timeout',
};
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,15 @@
import 'package:dio/dio.dart';
class BadNetworkError extends DioException {
final DioException dioError;
BadNetworkError(this.dioError)
: super(
requestOptions: dioError.requestOptions,
error: dioError.error,
response: dioError.response,
type: dioError.type,
message: dioError.message,
stackTrace: dioError.stackTrace,
);
}
@@ -0,0 +1,16 @@
import 'package:dio/dio.dart';
class BadRequestError extends DioException {
final DioException dioError;
final String? messageError;
BadRequestError(this.dioError, this.messageError)
: super(
error: dioError.error,
requestOptions: dioError.requestOptions,
response: dioError.response,
type: dioError.type,
message: dioError.message,
stackTrace: dioError.stackTrace,
);
}
@@ -0,0 +1,15 @@
import 'package:dio/dio.dart';
class ConnectionTimeoutError extends DioException {
final DioException dioError;
ConnectionTimeoutError(this.dioError)
: super(
error: dioError.error,
requestOptions: dioError.requestOptions,
response: dioError.response,
type: dioError.type,
message: dioError.message,
stackTrace: dioError.stackTrace,
);
}
@@ -0,0 +1,15 @@
import 'package:dio/dio.dart';
class InternalServerError extends DioException {
final DioException dioError;
InternalServerError(this.dioError)
: super(
requestOptions: dioError.requestOptions,
error: dioError.error,
response: dioError.response,
type: dioError.type,
message: dioError.message,
stackTrace: dioError.stackTrace,
);
}
@@ -0,0 +1,16 @@
import 'package:dio/dio.dart';
class NotFoundError extends DioException {
final DioException dioError;
final String? messageError;
NotFoundError(this.dioError, this.messageError)
: super(
error: dioError.error,
requestOptions: dioError.requestOptions,
response: dioError.response,
type: dioError.type,
message: dioError.message,
stackTrace: dioError.stackTrace,
);
}
@@ -0,0 +1,16 @@
import 'package:dio/dio.dart';
class UnauthorizedError extends DioException {
final DioException dioError;
final String? messageError;
UnauthorizedError(this.dioError, this.messageError)
: super(
requestOptions: dioError.requestOptions,
error: dioError.error,
response: dioError.response,
type: dioError.type,
message: dioError.message,
stackTrace: dioError.stackTrace,
);
}
@@ -0,0 +1,22 @@
import 'package:dio/dio.dart';
import '../../../injection.dart';
import '../../network/network_client.dart';
import '../errors/bad_network_error.dart';
class BadNetworkErrorInterceptor extends Interceptor {
final _networkClient = getIt<NetworkClient>();
@override
void onError(DioException err, ErrorInterceptorHandler handler) async {
final isConnected = await _networkClient.isConnected;
if (err.type == DioExceptionType.connectionTimeout ||
!isConnected ||
err.type == DioExceptionType.receiveTimeout ||
err.type == DioExceptionType.connectionError) {
return super.onError(BadNetworkError(err), handler);
}
super.onError(err, handler);
}
}
@@ -0,0 +1,15 @@
import 'package:dio/dio.dart';
import '../errors/bad_request_error.dart';
class BadRequestErrorInterceptor extends Interceptor {
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
if (err.response?.statusCode == 422 ||
err.response?.statusCode == 400 ||
err.response?.statusCode == 405) {
return super.onError(BadRequestError(err, null), handler);
}
super.onError(err, handler);
}
}
@@ -0,0 +1,13 @@
import 'package:dio/dio.dart';
import '../errors/connection_timeout_error.dart';
class ConnectionTimeoutErrorInterceptor extends Interceptor {
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
if (err.type == DioExceptionType.connectionTimeout) {
return super.onError(ConnectionTimeoutError(err), handler);
}
super.onError(err, handler);
}
}
@@ -0,0 +1,18 @@
import 'package:dio/dio.dart';
import '../errors/internal_server_error.dart';
class InternalServerErrorInterceptor extends Interceptor {
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
if (err.response != null) {
if (err.response?.statusCode != null &&
err.response!.statusCode! >= 500 &&
err.response!.statusCode! < 600) {
return super.onError(InternalServerError(err), handler);
}
}
super.onError(err, handler);
}
}
@@ -0,0 +1,13 @@
import 'package:dio/dio.dart';
import '../errors/not_found_error.dart';
class NotFoundErrorInterceptor extends Interceptor {
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
if (err.response?.statusCode == 404) {
return super.onError(NotFoundError(err, null), handler);
}
super.onError(err, handler);
}
}
@@ -0,0 +1,15 @@
import 'package:dio/dio.dart';
import '../errors/unauthorized_error.dart';
class UnauthorizedInterceptor extends Interceptor {
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
if (err.response?.statusCode == 401 ||
err.response?.statusCode == 403 ||
err.response?.statusCode == 419) {
return super.onError(UnauthorizedError(err, null), handler);
}
super.onError(err, handler);
}
}
+3
View File
@@ -0,0 +1,3 @@
class AppConstant {
static const String appName = "";
}
+9
View File
@@ -0,0 +1,9 @@
import 'package:injectable/injectable.dart';
import '../../presentation/router/app_router.dart';
@module
abstract class AutoRouteDi {
@lazySingleton
AppRouter get appRouter => AppRouter();
}
+8
View File
@@ -0,0 +1,8 @@
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:injectable/injectable.dart';
@module
abstract class ConnectivityDi {
@lazySingleton
Connectivity get connectivity => Connectivity();
}
+8
View File
@@ -0,0 +1,8 @@
import 'package:dio/dio.dart';
import 'package:injectable/injectable.dart';
@module
abstract class DioDi {
@lazySingleton
Dio get dio => Dio();
}
+8
View File
@@ -0,0 +1,8 @@
import 'package:injectable/injectable.dart';
import 'package:shared_preferences/shared_preferences.dart';
@module
abstract class SharedPreferencesDi {
@preResolve
Future<SharedPreferences> get prefs => SharedPreferences.getInstance();
}
+1
View File
@@ -0,0 +1 @@
// TODO: define your code
+8
View File
@@ -0,0 +1,8 @@
import 'package:flutter/material.dart';
void dismissKeyboard(BuildContext context) {
final currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
FocusManager.instance.primaryFocus?.unfocus();
}
}
+19
View File
@@ -0,0 +1,19 @@
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:injectable/injectable.dart';
@lazySingleton
class NetworkClient extends NetworkInfoBase {
final Connectivity connectivity;
NetworkClient(this.connectivity);
@override
Future<bool> get isConnected async {
final result = await connectivity.checkConnectivity();
return result.first != ConnectivityResult.none;
}
}
abstract class NetworkInfoBase {
Future<bool> get isConnected;
}
+5
View File
@@ -0,0 +1,5 @@
part of 'theme.dart';
class AppColor {
// TODO: define color
}
+5
View File
@@ -0,0 +1,5 @@
part of 'theme.dart';
class AppStyle {
// TODO: define style
}
+5
View File
@@ -0,0 +1,5 @@
part of 'theme.dart';
class AppValue {
// TODO: define value
}
+11
View File
@@ -0,0 +1,11 @@
import 'package:flutter/material.dart';
part 'app_color.dart';
part 'app_style.dart';
part 'app_value.dart';
class ThemeApp {
static ThemeData get theme => ThemeData(
useMaterial3: true,
);
}
+20
View File
@@ -0,0 +1,20 @@
import 'package:injectable/injectable.dart';
abstract class Env {
String get baseUrl;
// add getter here...
}
@Injectable(as: Env)
@dev
class DevEnv implements Env {
@override
String get baseUrl => ''; // example value
}
@Injectable(as: Env)
@prod
class ProdEnv implements Env {
@override
String get baseUrl => '';
}
+68
View File
@@ -0,0 +1,68 @@
// dart format width=80
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// InjectableConfigGenerator
// **************************************************************************
// ignore_for_file: type=lint
// coverage:ignore-file
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:apskel_owner_flutter/common/api/api_client.dart' as _i115;
import 'package:apskel_owner_flutter/common/di/di_auto_route.dart' as _i311;
import 'package:apskel_owner_flutter/common/di/di_connectivity.dart' as _i586;
import 'package:apskel_owner_flutter/common/di/di_dio.dart' as _i103;
import 'package:apskel_owner_flutter/common/di/di_shared_preferences.dart'
as _i402;
import 'package:apskel_owner_flutter/common/network/network_client.dart'
as _i543;
import 'package:apskel_owner_flutter/env.dart' as _i6;
import 'package:apskel_owner_flutter/presentation/router/app_router.dart'
as _i258;
import 'package:connectivity_plus/connectivity_plus.dart' as _i895;
import 'package:dio/dio.dart' as _i361;
import 'package:get_it/get_it.dart' as _i174;
import 'package:injectable/injectable.dart' as _i526;
import 'package:shared_preferences/shared_preferences.dart' as _i460;
const String _dev = 'dev';
const String _prod = 'prod';
extension GetItInjectableX on _i174.GetIt {
// initializes the registration of main-scope dependencies inside of GetIt
Future<_i174.GetIt> init({
String? environment,
_i526.EnvironmentFilter? environmentFilter,
}) async {
final gh = _i526.GetItHelper(this, environment, environmentFilter);
final sharedPreferencesDi = _$SharedPreferencesDi();
final dioDi = _$DioDi();
final autoRouteDi = _$AutoRouteDi();
final connectivityDi = _$ConnectivityDi();
await gh.factoryAsync<_i460.SharedPreferences>(
() => sharedPreferencesDi.prefs,
preResolve: true,
);
gh.lazySingleton<_i361.Dio>(() => dioDi.dio);
gh.lazySingleton<_i258.AppRouter>(() => autoRouteDi.appRouter);
gh.lazySingleton<_i895.Connectivity>(() => connectivityDi.connectivity);
gh.lazySingleton<_i543.NetworkClient>(
() => _i543.NetworkClient(gh<_i895.Connectivity>()),
);
gh.factory<_i6.Env>(() => _i6.DevEnv(), registerFor: {_dev});
gh.lazySingleton<_i115.ApiClient>(
() => _i115.ApiClient(gh<_i361.Dio>(), gh<_i6.Env>()),
);
gh.factory<_i6.Env>(() => _i6.ProdEnv(), registerFor: {_prod});
return this;
}
}
class _$SharedPreferencesDi extends _i402.SharedPreferencesDi {}
class _$DioDi extends _i103.DioDi {}
class _$AutoRouteDi extends _i311.AutoRouteDi {}
class _$ConnectivityDi extends _i586.ConnectivityDi {}
+9
View File
@@ -0,0 +1,9 @@
import 'package:get_it/get_it.dart';
import 'package:injectable/injectable.dart';
import 'injection.config.dart';
final getIt = GetIt.instance;
@InjectableInit()
Future<void> configureDependencies(String env) => getIt.init(environment: env);
+20 -114
View File
@@ -1,122 +1,28 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:injectable/injectable.dart';
void main() {
runApp(const MyApp());
}
import 'injection.dart';
import 'presentation/app_widget.dart';
class MyApp extends StatelessWidget {
const MyApp({super.key});
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// TRY THIS: Try running your application with "flutter run". You'll see
// the application has a purple toolbar. Then, without quitting the app,
// try changing the seedColor in the colorScheme below to Colors.green
// and then invoke "hot reload" (save your changes or press the "hot
// reload" button in a Flutter-supported IDE, or press "r" if you used
// the command line to start the app).
//
// Notice that the counter didn't reset back to zero; the application
// state is not lost during the reload. To reset the state, use hot
// restart instead.
//
// This works for code too, not just values: Most code changes can be
// tested with just a hot reload.
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
),
);
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
if (kReleaseMode) {
debugPrint = (message, {wrapWidth}) => '';
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// TRY THIS: Try changing the color here to a specific color (to
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
// change color while the other colors stay the same.
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
//
// TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
// action in the IDE, or press "p" in the console), to see the
// wireframe for each widget.
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
await configureDependencies(
kReleaseMode ? Environment.prod : Environment.dev,
);
runApp(const AppWidget());
}
+30
View File
@@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import '../common/theme/theme.dart';
import '../common/constant/app_constant.dart';
import '../injection.dart';
import 'router/app_router.dart';
import 'router/app_router_observer.dart';
class AppWidget extends StatefulWidget {
const AppWidget({super.key});
@override
State<AppWidget> createState() => _AppWidgetState();
}
class _AppWidgetState extends State<AppWidget> {
final _appRouter = getIt<AppRouter>();
@override
Widget build(BuildContext context) {
return MaterialApp.router(
debugShowCheckedModeBanner: false,
title: AppConstant.appName,
theme: ThemeApp.theme,
routerConfig: _appRouter.config(
navigatorObservers: () => <NavigatorObserver>[AppRouteObserver()],
),
);
}
}
@@ -0,0 +1,116 @@
// dart format width=80
/// GENERATED CODE - DO NOT MODIFY BY HAND
/// *****************************************************
/// FlutterGen
/// *****************************************************
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: deprecated_member_use,directives_ordering,implicit_dynamic_list_literal,unnecessary_import
import 'package:flutter/widgets.dart';
class $AssetsImagesGen {
const $AssetsImagesGen();
/// File path: assets/images/logo.png
AssetGenImage get logo => const AssetGenImage('assets/images/logo.png');
/// List of all assets
List<AssetGenImage> get values => [logo];
}
class Assets {
const Assets._();
static const $AssetsImagesGen images = $AssetsImagesGen();
}
class AssetGenImage {
const AssetGenImage(
this._assetName, {
this.size,
this.flavors = const {},
this.animation,
});
final String _assetName;
final Size? size;
final Set<String> flavors;
final AssetGenImageAnimation? animation;
Image image({
Key? key,
AssetBundle? bundle,
ImageFrameBuilder? frameBuilder,
ImageErrorWidgetBuilder? errorBuilder,
String? semanticLabel,
bool excludeFromSemantics = false,
double? scale,
double? width,
double? height,
Color? color,
Animation<double>? opacity,
BlendMode? colorBlendMode,
BoxFit? fit,
AlignmentGeometry alignment = Alignment.center,
ImageRepeat repeat = ImageRepeat.noRepeat,
Rect? centerSlice,
bool matchTextDirection = false,
bool gaplessPlayback = true,
bool isAntiAlias = false,
String? package,
FilterQuality filterQuality = FilterQuality.medium,
int? cacheWidth,
int? cacheHeight,
}) {
return Image.asset(
_assetName,
key: key,
bundle: bundle,
frameBuilder: frameBuilder,
errorBuilder: errorBuilder,
semanticLabel: semanticLabel,
excludeFromSemantics: excludeFromSemantics,
scale: scale,
width: width,
height: height,
color: color,
opacity: opacity,
colorBlendMode: colorBlendMode,
fit: fit,
alignment: alignment,
repeat: repeat,
centerSlice: centerSlice,
matchTextDirection: matchTextDirection,
gaplessPlayback: gaplessPlayback,
isAntiAlias: isAntiAlias,
package: package,
filterQuality: filterQuality,
cacheWidth: cacheWidth,
cacheHeight: cacheHeight,
);
}
ImageProvider provider({AssetBundle? bundle, String? package}) {
return AssetImage(_assetName, bundle: bundle, package: package);
}
String get path => _assetName;
String get keyName => _assetName;
}
class AssetGenImageAnimation {
const AssetGenImageAnimation({
required this.isAnimation,
required this.duration,
required this.frames,
});
final bool isAnimation;
final Duration duration;
final int frames;
}
@@ -0,0 +1,16 @@
// dart format width=80
/// GENERATED CODE - DO NOT MODIFY BY HAND
/// *****************************************************
/// FlutterGen
/// *****************************************************
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: deprecated_member_use,directives_ordering,implicit_dynamic_list_literal,unnecessary_import
class FontFamily {
FontFamily._();
/// Font family: Quicksand
static const String quicksand = 'Quicksand';
}
@@ -0,0 +1 @@
// TODO: define your code
+21
View File
@@ -0,0 +1,21 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
@RoutePage()
class SplashPage extends StatefulWidget {
const SplashPage({super.key});
@override
State<SplashPage> createState() => _SplashPageState();
}
class _SplashPageState extends State<SplashPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text("Splash Page"),
),
);
}
}
+11
View File
@@ -0,0 +1,11 @@
import 'package:auto_route/auto_route.dart';
import 'app_router.gr.dart';
@AutoRouterConfig()
class AppRouter extends RootStackRouter {
@override
List<AutoRoute> get routes => [
// Splash
AutoRoute(page: SplashRoute.page, initial: true),
];
}
@@ -0,0 +1,30 @@
// dart format width=80
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// AutoRouterGenerator
// **************************************************************************
// ignore_for_file: type=lint
// coverage:ignore-file
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:apskel_owner_flutter/presentation/pages/splash_page.dart'
as _i1;
import 'package:auto_route/auto_route.dart' as _i2;
/// generated route for
/// [_i1.SplashPage]
class SplashRoute extends _i2.PageRouteInfo<void> {
const SplashRoute({List<_i2.PageRouteInfo>? children})
: super(SplashRoute.name, initialChildren: children);
static const String name = 'SplashRoute';
static _i2.PageInfo page = _i2.PageInfo(
name,
builder: (data) {
return const _i1.SplashPage();
},
);
}
@@ -0,0 +1,21 @@
import 'dart:developer';
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
class AppRouteObserver extends AutoRouterObserver {
@override
void didPush(Route route, Route? previousRoute) {
log('New route pushed: ${route.settings.name}');
}
@override
void didInitTabRoute(TabPageRoute route, TabPageRoute? previousRoute) {
log('Tab route visited: ${route.name}');
}
@override
void didChangeTabRoute(TabPageRoute route, TabPageRoute previousRoute) {
log('Tab route re-visited: ${route.name}');
}
}