hayiqu 1.0.2
hayiqu: ^1.0.2 copied to clipboard
Flutter Provider Based Starter Template
Hayiqu Starter Template #
Hayiqu includes structures that you will use in most of your projects. We recommend using Hayiqu instead of writing the same codes over and over again.
What does Hayiqu include? #
-
Provider for State Management and Dependency Injection with
GetItpackage along withBaseViewModelstructure, -
GoRouterpackage for navigation, -
DeviceInfoUtils,DeviceOrientationSettings, andPackageInfoUtilspackages to control the device, -
Different extensions for
Context,DateTime,Number, andString, -
SkeletonLoader widget structure for loading screens,
-
A customized and easy-to-use
HttpServicestructure based on Dio package, -
BaseThemestructure for application theme, -
gap,flutter_svg,cached_network_image,toastification,modal_bottom_sheetpackage, andfluid dialogwidget structure for UI, -
flutter_cache_managerpackage for cache management, -
cryptopackage for encryption,
Usage Examples #
To add facilitating features to the ViewModel structure created with Provider, you can extend the BaseViewModel structure.
Usage Examples #
You can extend the BaseViewModel structure to add convenient features to the ViewModel structure created with Provider.
// Example usage
class MyViewModel with BaseViewModel {
final _apiService = getIt<ApiRepository>();
List<Todo> _todos = [];
List<Todo> get todos => _todos;
HomeViewmodel() {
runBusyFuture(_getTodos());
}
Future<void> _getTodos() async {
_todos = await _apiService.getTodos();
}
}
You can use the GetIt package for Dependency Injection.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// HttpService is registered on GetIt by default.
await setupLocator(() {
// Register your GetIt service here
getIt.registerLazySingleton<ApiRepository>(() => ApiService());
});
runApp(const MyApp());
}
To get detailed information about the current device;
print(await DeviceInfoUtils().getPlatformName());
print(await DeviceInfoUtils().getDeviceId());
For device-oriented screens;
await DeviceOrientationSettings.screenVertical();
await DeviceOrientationSettings.screenHorizontal();
await DeviceOrientationSettings.screenRight();
await DeviceOrientationSettings.screenLeft();
To get information about the application;
await PackageInfoUtils().init();
print(await PackageInfoUtils().getAppVersion());
print(await PackageInfoUtils().getAppName());
For loading (waiting) screens with content;
SkeletonLoader(
loading: true,
...
child: const HomeScreen(),
);
To customize or use HttpService;
final _httpService = getIt<HttpService>();
_httpService.enableLogger(true);
_httpService.setBaseUrl(baseUrl);
_httpService.setHeaders(headers);
_httpService.dio.options.connectTimeout = Duration(seconds: 5);
_httpService.dio.interceptors.add(...dummyInterceptor);
// GET request (with cache)
await _httpService.get(endpoint, useCache: true);
// POST request
await _httpService.post(endpoint);
// PUT request
await _httpService.put(endpoint);
// DELETE request
await _httpService.delete(endpoint);
// Single file download
await _httpService.download(
url: url,
savePath: savePath,
onProgress: (received, total) {
// Progress handling
},
);
Error / Result Management for [Result]
Future<Result<int, FirebaseException>> getUserId() async {
try {
final userId = await _authService.getUserId();
return Result.value(input: userId);
} on FirebaseException catch (e) {
return Result.error(input: e);
}
}
final result = await getUserId();
if (result.hasError) {
result.requireError.log();
} else {
result.requireValue.log();
}
For application theme;
// app/theme.dart
class ThemeProvider extends BaseTheme {}
// main.dart
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<ThemeProvider>(create: (_) => ThemeProvider()),
],
builder: (context, child) {
final themeProvider = context.watch<ThemeProvider>();
return MaterialApp.router(
title: 'Example',
theme: themeProvider.themeData,
themeMode: themeProvider.themeMode,
routerConfig: AppRouter.router,
debugShowCheckedModeBanner: false,
);
},
);
}
}
Gap usage
Row(
children: [
Gap(10),
Text('Hello'),
Gap(10),
],
)
...
Column(
children: [
Gap(5),
Text('World'),
Gap(5),
],
)
Fluid Dialog usage
OutlinedButton(
onPressed: () {
showDialog(
context: context,
builder: (context) => FluidDialog(
rootPage: FluidDialogPage(
alignment: Alignment.bottomCenter,
builder: (context) => const TestDialog(),
),
),
);
},
child: const Text('Open Dialog'),
),
//--- TestDialog Widget
TextButton(
onPressed: () => DialogNavigator.of(context).push(
FluidDialogPage(
// New dialog page to be displayed
builder: (context) => const SecondDialogPage(),
),
),
child: const Text('Go to next page'),
),
Figma-style text with stroke effect
TextWithStroke(
text: 'Flutter with Stroke',
style: TextStyle(
fontSize: 120,
color: Color(0xFF7DCFFF),
),
),
RichTextWidget
body: Padding(
padding: const EdgeInsets.all(8.0),
child: RichTextWidget(
styleForAll: Theme.of(context).textTheme.displayMedium,
texts: [
BaseText.plain(
text: 'Welcome to my blog at ',
),
BaseText.link(
text: 'https://example.com/',
onTap: () {
'Tapped'.log();
},
),
],
),
);
),
Useful Extensions #
String Extensions #
"exAmpLE".capitalize; // 'Example'
"example@gmail.com".isMail; // true
"örNek".toLowerCaseTurkish; // 'örnek'
"örNek".toUpperCaseTurkish; // 'ÖRNEK'
"örNek".onlyLatin; // 'rNek'
"<h1>Example</h1>".removeHtmlTags; // 'Example'
"Example text".countWords; // 2
"exam47p34le".removeNumbers; // 'example'
"exa1m24pl3e".onlyNumbers; // '1243'
"(example/!@#\$%^".removeSpecialChars; // 'example'
"es4e5523nt1is".removeLetters; // '455231'
"sLuG Case".toSlug; // 'sLuG_Case'
"SNAKE CASE".toSnakeCase; // 'snake_case'
"Find max of array".toCamelCase; // 'findMaxOfArray'
"Hello my name is example".toTitleCase; // 'Hello My Name Is Example'
// Reads the file on the Assets
// Returns Uint8List data
await 'images/template.png'.localFileData())
DateTime Extensions #
DateTime.now.passingTime(DateTime.now().add(Duration(days: 1))); // '1 gün önce'
DateTime.now.passingTime(DateTime.now().add(Duration(days: 30))); // '30 gün önce'
DateTime.now.passingTimeSort(DateTime.now().add(Duration(days: 1))); // '1g'
DateTime.now.passingTimeSort(DateTime.now().add(Duration(days: 30))); // '30g'
Number Extensions #
12.sbh(); // SizedBox(height: 12)
12.sbw(); // SizedBox(width: 12)
0.2.vw; // Screen Width * 0.2
0.2.vh; // Screen Height * 0.2
Context Extensions #
context.colors; // Theme.of(context).colorScheme
context.textTheme; // Theme.of(context).textTheme
context.mediaQuery; // MediaQuery.of(context)
context.height; // MediaQuery.sizeOf(context).height
context.width; // MediaQuery.sizeOf(context).width
context.lowValue; // context.height * 0.01
context.defaultValue; // context.height * 0.02
context.highValue; // context.height * 0.05
context.veryhighValue1x; // context.height * 0.1
context.veryhighValue2x; // context.height * 0.2
context.veryhighValue3x; // context.height * 0.3
context.veryhighValue4x; // context.height * 0.4
context.veryhighValue5x; // context.height * 0.5
context.dynamicWidth(0.2); // context.width * 0.2
context.dynamicHeight(0.2); // context.height * 0.2
context.paddingAllLow; // EdgeInsets.all(context.lowValue)
context.paddingAllDefault; // EdgeInsets.all(context.defaultValue)
context.paddingAllHigh; // EdgeInsets.all(context.highValue)
context.paddingHorizontalLow; // EdgeInsets.symmetric(horizontal: context.lowValue)
context.paddingHorizontalDefault; // EdgeInsets.symmetric(horizontal: context.defaultValue)
context.paddingHorizontalHigh; // EdgeInsets.symmetric(horizontal: context.highValue)
context.paddingVerticalLow; // EdgeInsets.symmetric(vertical: context.lowValue)
context.paddingVerticalDefault; // EdgeInsets.symmetric(vertical: context.defaultValue)
context.paddingVerticalHigh; // EdgeInsets.symmetric(vertical: context.highValue)
... and more
Function Extensions #
// Waiting operation in VoidCallback functions
_counterClockwiseRotationController.forward.delayedCall(
const Duration(
seconds: 1,
),
);
// Future Unwrap function (Null Safety)
static Future<File> pickImageFromGallery() => _imagePicker
.pickImage(source: ImageSource.gallery)
.unwrap() // null check
.then((xFile) => xFile.path)
.then((filePath) => File(filePath))
// Future.futureBuilder() function
// This function performs the same function as FutureBuilde
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
Future<String> getName() => Future.delayed(
const Duration(seconds: 2),
() => 'Example',
);
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: getName().futureBuilder(
onData: (_, name) => Text(name),
onNone: onNone,
// onError: ..,
// onDoneWithoutDataOrError: ..,
// onWaiting: ..,
),
),
);
}
}
Stream Extensions #
// Waiting operation between events
await for (final name in getNames().withTimeoutBetweenEvents(
const Duration(
seconds: 3,
),
)) {
// object.log extension
name.log();
}
// Stream Error Recovery operation
Stream<String> getNames() async* {
yield 'Foo';
yield 'Bar';
throw Exception('Something went wrong');
}
final names = getNames().onErrorRecoverWith(
(error) {
error.log();
return 'Baz';
},
);
await for (final name in names) {
name.log(); // Foo, Bar, Baz
}
// Stopping the Stream itself if an error occurs
Stream<String> getNames() async* {
yield 'Vandad';
await Future.delayed(const Duration(seconds: 1));
yield 'John';
await Future.delayed(const Duration(seconds: 1));
throw 'Enough names for you';
}
Future<void> testIt() async {
await for (final name in getNames().absorbErrors()) {
name.log(); // Vandad, John, then stream closes
}
}
Object Extensions #
// Flattening the list
void testIt() {
final flat = [
[[1, 2, 3], 4, 5],
[6, [7, [8, 9]], 10],
11,12
].flatten().log();
// (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
}
// Advanced Where filtering operation on Map object
people.where((key, value) => key.length > 4 && value > 20).log();
// {Peter: 22}
people.whereKey((key) => key.length < 5).log();
// {John: 20, Mary: 21}
people.whereValue((value) => value.isEven).log();
// {John: 20, Peter: 22}