1686227236
A Pet Adoption appllication using Flutter
.gitignore
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
.metadata
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.
version:
revision: 350d2c3a6ca4c3c39787e1c18a8ddf0aca8d2ae2
channel: beta
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 350d2c3a6ca4c3c39787e1c18a8ddf0aca8d2ae2
base_revision: 350d2c3a6ca4c3c39787e1c18a8ddf0aca8d2ae2
- platform: android
create_revision: 350d2c3a6ca4c3c39787e1c18a8ddf0aca8d2ae2
base_revision: 350d2c3a6ca4c3c39787e1c18a8ddf0aca8d2ae2
- platform: ios
create_revision: 350d2c3a6ca4c3c39787e1c18a8ddf0aca8d2ae2
base_revision: 350d2c3a6ca4c3c39787e1c18a8ddf0aca8d2ae2
- platform: web
create_revision: 350d2c3a6ca4c3c39787e1c18a8ddf0aca8d2ae2
base_revision: 350d2c3a6ca4c3c39787e1c18a8ddf0aca8d2ae2
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
Author: bamakant
Source: https://github.com/bamakant/pet_adoption_app
1686219780
A Flutter package for retrieving country information based on a user's public IP address.
Get the user's public IP address. Retrieve country information such as country code, country name, and ISP (Internet Service Provider).
To use this package, follow the steps below:
Add the package to your pubspec.yaml file:
dependencies:
ip_country_lookup: ^1.0.0
Import the package in your Dart file:
import 'package:ip_country_lookup/ip_country_lookup.dart';
Get the user's public IP address:
String publicIp = await IpCountryLookup().getUserIpAddress();
Retrieve country information using the user's IP address:
IpCountryData countryData = await IpCountryLookup().getIpLocationData();
Note: Ensure that you have the necessary permissions and internet connectivity to retrieve the user's IP address and access the country information.
import 'package:flutter/material.dart';
import 'package:ip_country_lookup/ip_country_lookup.dart';
import 'package:ip_country_lookup/models/ip_country_data_model.dart';
void main(List<String> args) {
runApp(const IpCountryLookupExampleApp());
}
class IpCountryLookupExampleApp extends StatelessWidget {
const IpCountryLookupExampleApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: MainScreen(),
);
}
}
class MainScreen extends StatefulWidget {
const MainScreen({super.key});
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
bool isDataLoaded = false;
bool isLoading = false;
IpCountryData? countryData;
String? usersPublicIpAddress;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(
"IP Country Lookup",
),
),
body: SizedBox(
width: MediaQuery.of(context).size.width,
child: isLoading
? const Center(
child: CircularProgressIndicator(),
)
: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
isDataLoaded
? Padding(
padding: const EdgeInsets.symmetric(horizontal: 44),
child: Column(
children: [
Text(
"Country name: ${countryData!.country_name.toString()}",
),
Text(
"Country code: ${countryData!.country_code.toString()}",
),
const SizedBox(
height: 30,
),
const Divider(),
const SizedBox(
height: 30,
),
Text(
"Users public ip: ${countryData!.ip.toString()}",
),
Text(
"Users ISP: ${countryData!.isp.toString()}",
),
const SizedBox(
height: 30,
),
],
),
)
: const SizedBox.shrink(),
ElevatedButton(
onPressed: () async {
setState(() {
isLoading = true;
});
countryData = await IpCountryLookup().getIpLocationData();
setState(() {
isLoading = false;
isDataLoaded = true;
});
},
child: const Text(
"Get country data from IP",
),
),
],
),
),
);
}
}
For any issues, questions, or feature requests, please email me at: afridi.khondakar@gmail.com.
The ip_country_lookup package allows you to easily retrieve a user's public IP address and obtain country information such as the country code, country name, and ISP. It simplifies the process of identifying the user's location based on their IP address, enabling you to build geolocation-aware applications with ease.
Author: WorkWithAfridi
Source: https://github.com/WorkWithAfridi/IP_Country_Lookup-Package
License: MIT license
1686212280
TicketViewFlutter is a simple and customizable Ticket/Receipt View for Flutter. The source code is 100% Dart.
Motivation
I need some clean Ticket/Receipt View view for my Flutter application.
Getting started
Add this to your package's pubspec.yaml file:
This library is posted in pub.dev
dependencies:
sks_ticket_view: ^1.0.0
Usage
After Importing this library, you can directly use this view in your Widget tree
import 'package:ticketview/ticketview.dart';
Default Ticket View
TicketView(
child: Container(),
)
Customized Receipt View
SKSTicketView(
backgroundPadding: EdgeInsets.symmetric(vertical: 0, horizontal: 20),
backgroundColor: Color(0xFF8F1299),
contentPadding: EdgeInsets.symmetric(vertical: 24, horizontal: 0),
drawArc: false,
triangleAxis: Axis.vertical,
borderRadius: 6,
drawDivider: true,
trianglePos: .5,
child: Container(),
)
Customization
Depending on your view you may want to tweak the UI. For now you can these custom attributes
Property | Type | Description |
---|---|---|
backgroundColor | Color | Background card color for TicketView |
contentBackgroundColor | Color | Content card color for TicketView |
Screenshots
Author
Author: longhoang2984
Source: https://github.com/longhoang2984/ticket_view
License: BSD-3-Clause license
1686204804
.gitignore
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
.metadata
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 1aafb3a8b9b0c36241c5f5b34ee914770f015818
channel: stable
project_type: app
Author: sharvayasupport
Source: https://github.com/sharvayasupport/GroceryShopFlutter
1686199200
A new Flutter project for weather forecasting.
Packages used
App UI
.gitignore
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
.metadata
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.
version:
revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
channel: stable
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
- platform: android
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
- platform: ios
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
- platform: linux
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
- platform: macos
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
- platform: web
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
- platform: windows
create_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
base_revision: 90c64ed42ba53a52d18f0cb3b17666c8662ed2a0
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
Author: Harpreetsingh10414
Source: https://github.com/Harpreetsingh10414/WeatherX
1686197400
Inway is an app inspired by Uber, designed to provide a similar ride-hailing experience. With Inway, users can easily request rides, track their drivers, and reach their destinations conveniently. The app leverages the power of Google Maps to display real-time locations and optimize route navigation. It also utilizes Firebase as the backend to enable seamless phone-based signup and comprehensive profile management.
Clone the repository and run the following commands:
flutter pub get
flutter run
Intro | SingUp | Login |
---|---|---|
home | map | activity |
---|---|---|
Profile | Setting | edit |
---|---|---|
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
.metadata
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.
version:
revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
channel: stable
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
- platform: android
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
- platform: ios
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
- platform: linux
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
- platform: macos
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
- platform: web
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
- platform: windows
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
Author: mohamed352
Source: https://github.com/mohamed352/Inway
1686148370
mutex
A library for creating locks to ensure mutual exclusion when running critical sections of code.
Mutexes can be used to protect critical sections of code to prevent race conditions.
Although Dart uses a single thread of execution, race conditions can still occur when asynchronous operations are used inside critical sections. For example,
x = 42;
synchronousOperations(); // this does not modify x
assert(x == 42); // x will NOT have changed
y = 42; // a variable that other asynchronous code can modify
await asynchronousOperations(); // this does NOT modify y, but...
// There is NO GUARANTEE other async code didn't run and change it!
assert(y == 42 || y != 42); // WARNING: y might have changed
An example is when Dart is used to implement a server-side Web server that updates a database (assuming database transactions are not being used). The update involves querying the database, performing calculations on those retrieved values, and then updating the database with the result. You don't want the database to be changed by "something else" while performing the calculations, since the results you would write will not incorporate those other changes. That "something else" could be the same Web server handling another request in parallel.
This package provides a normal mutex and a read-write mutex.
A mutex guarantees at most only one lock can exist at any one time.
If the lock has already been acquired, attempts to acquire another lock will be blocked until the lock has been released.
import 'package:mutex/mutex.dart';
...
final m = Mutex();
Acquiring the lock before running the critical section of code, and then releasing the lock.
await m.acquire();
// No other lock can be acquired until the lock is released
try {
// critical section with asynchronous code
await ...
} finally {
m.release();
}
The following code uses the protect convenience method to do the same thing as the above code. Use the convenence method whenever possible, since it ensures the lock will always be released.
await m.protect(() async {
// critical section
});
If the critial section returns a Future to a value, the protect convenience method will return a Future to that value.
final result = await m.protect<int>(() async {
// critical section
return valueFromCriticalSection;
});
// result contains the valueFromCriticalSection
A read-write mutex allows multiple reads locks to be exist simultaneously, but at most only one write lock can exist at any one time. A write lock and any read locks cannot both exist together at the same time.
If there is one or more read locks, attempts to acquire a write lock will be blocked until all the read locks have been released. But attempts to acquire more read locks will not be blocked. If there is a write lock, attempts to acquire any lock (read or write) will be blocked until that write lock is released.
A read-write mutex can also be described as a single-writer mutex, multiple-reader mutex, or a reentrant lock.
import 'package:mutex/mutex.dart';
...
final m = ReadWriteMutex();
Acquiring a write lock:
await m.acquireWrite();
// No other locks (read or write) can be acquired until released
try {
// critical write section with asynchronous code
await ...
} finally {
m.release();
}
Acquiring a read lock:
await m.acquireRead();
// No write lock can be acquired until all read locks are released,
// but additional read locks can be acquired.
try {
// critical read section with asynchronous code
await ...
} finally {
m.release();
}
The following code uses the protectWrite and protectRead convenience methods to do the same thing as the above code. Use the convenence method whenever possible, since it ensures the lock will always be released.
await m.protectWrite(() async {
// critical write section
});
await m.protectRead(() async {
// critical read section
});
If the critial section returns a Future to a value, these convenience methods will return a Future to that value.
final result1 await m.protectWrite<String>(() async {
// critical write section
return valueFromCritialSection1;
});
// result1 contains the valueFromCriticalSection1
final result2 = await m.protectRead(() async {
// critical read section
return valueFromCritialSection2;
});
// result2 contains the valueFromCriticalSection2
The critical section should always contain some asynchronous code. If the critical section only contains synchronous code, there is no need to put it in a critical section. In Dart, synchronous code cannot be interrupted, so there is no need to protect it using mutual exclusion.
Also, if the critical section does not involve data or shared resources that can be accessed by other asynchronous code, it also does not need to be protected. For example, if it only uses local variables that other asynchronous code won't have access to: while the other asynchronous code could run, it won't be able to make unexpected changes to the local variables it can't access.
Please file feature requests and bugs at the issue tracker.
Run this command:
With Dart:
$ dart pub add mutex
With Flutter:
$ flutter pub add mutex
This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get
):
dependencies:
mutex: ^3.0.1
Alternatively, your editor might support dart pub get
or flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:mutex/mutex.dart';
// Mutex example.
//
// This example demonstrates why a mutex is needed.
import 'dart:async';
import 'dart:math';
import 'package:mutex/mutex.dart';
//----------------------------------------------------------------
// Random asynchronous delays to try and simulate race conditions.
const _maxDelay = 500; // milliseconds
final _random = Random();
Future<void> randomDelay() async {
await Future<void>.delayed(
Duration(milliseconds: _random.nextInt(_maxDelay)));
}
//----------------------------------------------------------------
/// Account balance.
///
/// The classical example of a race condition is when a bank account is updated
/// by different simultaneous operations.
int balance = 0;
//----------------------------------------------------------------
/// Deposit without using mutex.
Future<void> unsafeUpdate(int id, int depositAmount) async {
// Random delay before updating starts
await randomDelay();
// Add the deposit to the balance. But this operation is not atomic if
// there are asynchronous operations in it (as simulated by the randomDelay).
final oldBalance = balance;
await randomDelay();
balance = oldBalance + depositAmount;
print(' [$id] added $depositAmount to $oldBalance -> $balance');
}
//----------------------------------------------------------------
/// Deposit using mutex.
Mutex m = Mutex();
Future<void> safeUpdate(int id, int depositAmount) async {
// Random delay before updating starts
await randomDelay();
// Acquire the mutex before running the critical section of code
await m.protect(() async {
// critical section
// This is the same as the unsafe update. But since it is performed only
// when the mutex is acquired, it is safe: no other safe update can happen
// until this mutex is released.
final oldBalance = balance;
await randomDelay();
balance = oldBalance + depositAmount;
// end of critical section
print(' [$id] added $depositAmount to $oldBalance -> $balance');
});
}
//----------------------------------------------------------------
/// Make a series of deposits and see if the final balance is correct.
Future<void> makeDeposits({bool safe = true}) async {
print(safe ? 'Using mutex:' : 'Not using mutex:');
const numberDeposits = 10;
const amount = 10;
balance = 0;
// Create a set of operations, each attempting to deposit the same amount
// into the account.
final operations = <Future>[];
for (var x = 0; x < numberDeposits; x++) {
final f = (safe) ? safeUpdate(x, amount) : unsafeUpdate(x, amount);
operations.add(f);
}
// Wait for all the deposit operations to finish
await Future.wait<void>(operations);
// Check if all of the operations succeeded
final expected = numberDeposits * amount;
if (balance != expected) {
print('Error: deposits were lost (final balance $balance != $expected)');
} else {
print('Success: no deposits were lost');
}
}
//----------------------------------------------------------------
void main() async {
await makeDeposits(safe: false);
print('');
await makeDeposits(safe: true);
}
Download details:
Author: hoylen.com
Source: https://github.com/hoylen/dart-mutex
1686148087
A Flutter package for cropping any widget, not only images. This package is entirely written in Dart and supports Android, iOS, Web and Desktop. Also, because of being independent from native platform, it does not increase size of your apps output (e.g. apk).
Web Demo | Install from Google Play
If you find this project useful, please support me by buying me a pizza 🍕.
Tron Address:
TLtrEU4KT2bn5J87VWfs1QDrmB1aFQ1bja
Ethereum Address:
0xf8Da77e7BbE39be8c9e527289465Bf7219af58db
I do not accept Bitcoin due to its issues with sustainability and global warming.
In your pubspec.yaml
file add:
dependencies:
crop: any
Then, in your code import:
import 'package:crop/crop.dart';
Now in build function, put a Crop
widget in the widget tree and you are done. Please don't forget to check /example
folder, there is much more.
Run this command:
With Flutter:
$ flutter pub add crop
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
crop: ^0.5.5
Alternatively, your editor might support flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:crop/crop.dart';
import 'dart:ui' as ui;
import 'package:app/centered_slider_track_shape.dart';
import 'package:flutter/material.dart';
import 'package:crop/crop.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:permission_handler/permission_handler.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Crop Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
brightness: Brightness.dark,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final controller = CropController(aspectRatio: 1000 / 667.0);
double _rotation = 0;
BoxShape shape = BoxShape.rectangle;
void _cropImage() async {
final pixelRatio = MediaQuery.of(context).devicePixelRatio;
final cropped = await controller.crop(pixelRatio: pixelRatio);
if (cropped == null) {
return;
}
if (!mounted) {
return;
}
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => Scaffold(
appBar: AppBar(
title: const Text('Crop Result'),
centerTitle: true,
actions: [
Builder(
builder: (context) => IconButton(
icon: const Icon(Icons.save),
onPressed: () async {
final status = await Permission.storage.request();
if (status == PermissionStatus.granted) {
await _saveScreenShot(cropped);
if (!mounted) {
return;
}
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Saved to gallery.'),
),
);
}
},
),
),
],
),
body: Center(
child: RawImage(
image: cropped,
),
),
),
fullscreenDialog: true,
),
);
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(
title: const Text('Crop Demo'),
centerTitle: true,
leading: IconButton(
icon: const Icon(Icons.link),
onPressed: () {
launchUrl(Uri.parse('https://github.com/xclud/flutter_crop'),
mode: LaunchMode.externalApplication);
},
),
actions: <Widget>[
IconButton(
onPressed: _cropImage,
tooltip: 'Crop',
icon: const Icon(Icons.crop),
)
],
),
body: Column(
children: <Widget>[
Expanded(
child: Container(
color: Colors.black,
padding: const EdgeInsets.all(8),
child: Crop(
onChanged: (decomposition) {
if (_rotation != decomposition.rotation) {
setState(() {
_rotation = ((decomposition.rotation + 180) % 360) - 180;
});
}
// print(
// "Scale : ${decomposition.scale}, Rotation: ${decomposition.rotation}, translation: ${decomposition.translation}");
},
controller: controller,
shape: shape,
/* It's very important to set `fit: BoxFit.cover`.
Do NOT remove this line.
There are a lot of issues on github repo by people who remove this line and their image is not shown correctly.
*/
foreground: IgnorePointer(
child: Container(
alignment: Alignment.bottomRight,
child: const Text(
'Foreground Object',
style: TextStyle(color: Colors.red),
),
),
),
helper: shape == BoxShape.rectangle
? Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.white, width: 2),
),
)
: null,
child: Image.asset(
'images/sample.jpg',
fit: BoxFit.cover,
),
),
),
),
Row(
children: <Widget>[
IconButton(
icon: const Icon(Icons.undo),
tooltip: 'Undo',
onPressed: () {
controller.rotation = 0;
controller.scale = 1;
controller.offset = Offset.zero;
setState(() {
_rotation = 0;
});
},
),
Expanded(
child: SliderTheme(
data: theme.sliderTheme.copyWith(
trackShape: CenteredRectangularSliderTrackShape(),
),
child: Slider(
divisions: 360,
value: _rotation,
min: -180,
max: 180,
label: '$_rotation°',
onChanged: (n) {
setState(() {
_rotation = n.roundToDouble();
controller.rotation = _rotation;
});
},
),
),
),
PopupMenuButton<BoxShape>(
icon: const Icon(Icons.crop_free),
itemBuilder: (context) => [
const PopupMenuItem(
value: BoxShape.rectangle,
child: Text("Box"),
),
const PopupMenuItem(
value: BoxShape.circle,
child: Text("Oval"),
),
],
tooltip: 'Crop Shape',
onSelected: (x) {
setState(() {
shape = x;
});
},
),
PopupMenuButton<double>(
icon: const Icon(Icons.aspect_ratio),
itemBuilder: (context) => [
const PopupMenuItem(
value: 1000 / 667.0,
child: Text("Original"),
),
const PopupMenuDivider(),
const PopupMenuItem(
value: 16.0 / 9.0,
child: Text("16:9"),
),
const PopupMenuItem(
value: 4.0 / 3.0,
child: Text("4:3"),
),
const PopupMenuItem(
value: 1,
child: Text("1:1"),
),
const PopupMenuItem(
value: 3.0 / 4.0,
child: Text("3:4"),
),
const PopupMenuItem(
value: 9.0 / 16.0,
child: Text("9:16"),
),
],
tooltip: 'Aspect Ratio',
onSelected: (x) {
controller.aspectRatio = x;
setState(() {});
},
),
],
),
],
),
);
}
}
Future<dynamic> _saveScreenShot(ui.Image img) async {
var byteData = await img.toByteData(format: ui.ImageByteFormat.png);
var buffer = byteData!.buffer.asUint8List();
final result = await ImageGallerySaver.saveImage(buffer);
return result;
}
Download details:
Author: pwa.ir
Source: https://github.com/xclud/flutter_crop
#flutter #android #web-development #web #ios #mobile #image #dart
1686146811
This is a pure dart package and Algorithm is based on popular JavaScript library jalaali-js with more than 20k monthly downloads.
This package has a lot of unit tests with high test coverage for ensuring its correctness.
+
and -
operators.^
operator.As of version 1.0.1
there is time
getter for Jalali and Gregorian to acquire time information in Duration.
As of version 1.0.0
Jalali and Gregorian include time information.
If you want a new feature, or you found an issue, please make an issue on GitHub, so I can see your request.
Add it to your pubspec.yaml file:
dependencies:
shamsi_date: ^latest.version
Then depend on it:
import 'package:shamsi_date/shamsi_date.dart';
Jalali class is used for Shamsi (Jalali, Persian, شمسی or خورشیدی) date and Gregorian class is used for Gregorian (Miladi or میلادی) date. Jalali and Gregorian classes are the subclasses of Date.
Jalali and Gregorian can be instantiated with providing year
, month
and day
among other ways:
Jalali j = Jalali(year, month, day);
Gregorian g = Gregorian(year, month, day);
you can also provide time information with hour
, minute
, second
and millisecond
.
Jalali j = Jalali(year, month, day, hour, minute, second, millisecond);
Gregorian g = Gregorian(year, month, day, hour, minute, second, millisecond);
Month and day has default value of 1
if you don't specify them, so Jalali(year, month)
is equivalent to Jalali(year, month, 1)
and Gregorian(year)
is equivalent to Gregorian(year, 1, 1)
. And also hour, minute, second and millisecond will default to 0
if not provided.
Constructor arguments should be non-null or exception will be thrown immediately. This ensures objects being in valid state when created. So year, month and day are always non-null. Almost all methods, operators, constructors and factories should have non-null arguments, and they will return non-null objects. For example year, month and day getters will return non-null results. The only exception for methods which can accept null arguments are methods with optional arguments like add(...)
and copy(...)
. in nullsafe version: nullable and non-nullable argument and return types are checked statically.
All created date instances are valid. When creating a date instance either by using constructors and factories or by using methods and operators on an existing date instance, if the new date is invalid (by its month or day being out of range), or it is out of computable range, a DateException exception is thrown. So if you think the new date instance can become invalid or out of range you should surround it with try-catch and catching DateException
. Minimum computable date is Gregorian(560,3,20)
or equivalently Jalali(-61,1,1)
and Maximum computable date is Gregorian(3798,12,31)
or equivalently Jalali(3177,10,11)
. For example:
void main() {
try {
Jalali jv = Jalali(1398, 13, 1); // not valid!
} on DateException catch (e) {
// prints: DateException: Jalali month is out of valid range.
print(e);
}
}
Jalali and Gregorian objects are immutable. So using operators and methods will give you new object and does not manipulate the object in place, like String objects. Almost all other objects in shamsi_date library are immutable too.
You can access year
, month
, day
, hour
, minute
, second
and millisecond
through getters on Jalali or Gregorian dates. You can get week day number of Jalali and Gregorian by using weekDay
getter. Week days range from 1 to 7. Jalali week starts with Shanbe
and Gregorian week starts with Monday
. Month length can be accessed using monthLength
getter. Month length is sensitive to leap years. you can check if the year is a leap year by isLeapYear()
method. Julian day number is also accessible through julianDayNumber
getter. for example:
Jalali j = Jalali(1397, 5, 6, 12, 56, 34, 585);
int jy = j.year; // 1397
int jm = j.month; // 5
int jd = j.day; // 6
int jth = j.hour; // 12
int jtm = j.minute; // 56
int jts = j.second; // 34
int jtms = j.millisecond; // 585
int wd = j.weekDay; // wd = 1 (Shanbe)
// month length of 1397/5
// note: day value is not important for monthLength
int ml = j.monthLength; // ml = 31
// check if 1397 is a leap year
// note: month and day values are not important for isLeapYear() method
bool ly = j.isLeapYear(); // ly = false (1397 is not leap year)
// and equivalently for Gregorian date objects ...
You can convert Jalali dates to Gregorian by using toGregorian()
method and convert Gregorian to Jalali date by using toJalali()
method. There are also factory methods Jalali.fromGregorian(...)
and Gregorian.fromJalali(...)
which can be used alternatively.
Jalali j = Jalali(1397, 5, 6);
// convert to Gregorian:
Gregorian j2g1 = j.toGregorian(); // -> 2018/8/28
// or equivalently:
Gregorian j2g2 = Gregorian.fromJalali(j);
Gregorian g = Gregorian(2019, 10, 26);
// convert to Jalali:
Jalali g2j1 = g.toJalali(); // -> 1398/8/4
// or equivalently:
Jalali g2j2 = Jalali.fromGregorian(g);
You can convert DateTime objects directly to Jalali or Gregorian dates by using fromDateTime(dateTime)
static methods. Convert Jalali and Gregorian to DateTime by using toDateTime()
method. You can pass hour
, minute
and other time details to arguments. There is also toUtcDateTime
for UTC date times. Get Jalali and Gregorian dates for now by using now()
factory.
// convert from DateTime
Jalali j = Jalali.fromDateTime(dateTime);
Gregorian g = Gregorian.fromDateTime(dateTime);
// convert to DateTime
DateTime j2dt = j.toDateTime();
DateTime g2dt = g.toDateTime();
// and also convert to UTC:
DateTime j2dt2 = j.toUtcDateTime();
// get now
Jalali jNow = Jalali.now();
Gregorian gNow = Gregorian.now();
For converting DateTime you can also use extension methods.
DateTime dt = DateTime.now();
Jalali j = dt.toJalali();
Gregorian g = dt.toGregorian();
Jalali and Georgian dates are immutable, so you can not change their properties in place. if you want only to change some fields of a Jalali or Gregorian date you can use copy(...)
method or withYear
, withMonth
and withDay
methods on an existing object. These methods can be chained. copy method changes all fields at one. note that copy and with*() methods are not safe, and it is your responsibility to avoid problems like month length bound (for example changing month of 31 Farvardin 1390
to Esfand
) or leap crash (for example being in last day of year in a leap year and changing year to a non-leap one) in intermediate steps. order of operations is important.
For example for getting date at start of this month in Jalali: (copy method makes another object instance and leaves the original one unchanged)
Jalali j1 = Jalali.now().withDay(1); // correct way
// or by using copy method:
Jalali j2 = Jalali.now().copy(day: 1); // also correct
// DON NOT do it like this:
Jalali j3 = Jalali(Jalali.now().year, Jalali.now().month, 1); // INCORRECT
Or if you want to get last day of the last month of this Jalali year:
// at first go to first day of last month: (Avoid leap crash)
Jalali tmp = Jalali.now().withDay(1).withMonth(12);
// since we can be in a leap year we use monthLength for going to last day:
Jalali j = tmp.withDay(tmp.monthLength);
// or by using copy method:
Jalali tmp1 = Jalali.now().copy(month: 12, day: 1);
Jalali j1 = tmp.copy(day: tmp1.monthLength);
or to find 3rd day of 2nd month of this year:
Jalali j = Jalali.now().withDay(3).withMonth(2);
// or by using copy method:
Jalali j1 = Jalali.now().copy(month: 2, day: 3);
Or If you want your Jalali and Gregorian objects to fall back to today if null is provided as their constructor arguments you can use copy method from now factory method, for example for Jalali:
Jalali j = Jalali.now().copy(year: y, month: m, day: d);
// y, m and d can be null
You can find distance between Jalali and Gregorian dates by using ^
operator. Note that -
operator is for something else. Or you can use distanceTo
and distanceFrom
methods.
int distance11 = Jalali.now() ^ Jalali(1395, 10, 1);
// or
int distance12 = Jalali.now().distanceFrom(Jalali(1395, 10, 1));
// or
int distance13 = Jalali(1395, 10, 1).distanceTo(Jalali.now());
// and similarly for Gregorian
int distance2 = Gregorian(2021) ^ Gregorian(2020);
You can add and subtract days to Jalali and Gregorian using +
and -
operators. It is guaranteed to give you a bound valid date. for example, it will go to next month or next year if needed, and they won't have leap crash.
You can add years, months or days to Jalali and Gregorian using addYears
, addMonths
and addDays
. These methods can be chained, and they will not have range crash. addDays
can change month and year. addMonths
can change year. note that it is your responsibility to avoid leap crash.
If you want you can add a combination of days, months or years to a date object with add
method. note that add
method is not safe and does not manipulate result to become bound valid, it is your responsibility. It is recommended to use addYear, addMonth and addDay methods over add method. note By using addYears, addMonth and addDay you can put day out of month length bounds. addMonth is safe for month overflow.
Jalali j1 = Jalali(1398, 8, 4);
// add days
Jalali j2 = j1 + 3; // -> 1398/8/7
// result will be manipulated to become valid:
Jalali j3 = j1 + 30; // -> 1398/9/4
Jalali j4 = j1 + 365; // -> 1399/8/4
// subtract days
Jalali j5 = j1 - 2; // -> 1398/8/2
// add years, months and days:
Jalali j6 = j1.addYears(1).addMonths(2).addDays(3); // 1399/10/7
// or:
Jalali j60 = j1.add(years: 1, months: 2, days: 3); // 1399/10/7
// add years and days only:
Jalali j7 = j1.addYears(1).addDays(3); // 1399/8/7
// or:
Jalali j70 = j1.add(years: 1, days: 3); // 1399/8/7
// add months only:
Jalali j8 = j1.addMonths(2); // 1398/10/3
// or:
Jalali j80 = j1.add(months: 2); // 1398/10/3
// if you want to subtract you can add negative value:
Jalali j9 = j1.addYears(-1); // 1397/8/3
// or:
Jalali j90 = j1.add(years: -1); // 1397/8/3
// addYears, addMonths and addDays methods are bound safe
// add(...) method is NOT bound safe
Date formatting is easy. You should make a function for custom formatting and then pass your Jalali or Gregorian dates to this function.
For example if you want to format as WeekDayName Day MonthName TwoDigitYear
you make a function for it:
String format1(Date d) {
final f = d.formatter;
return '${f.wN} ${f.d} ${f.mN} ${f.yy}';
}
// example output for Jalali: "پنج شنبه 21 دی 91"
// example output for Gregorian: "Thursday 10 January 13"
Or if you want to format as FourDigitYear/TwoDigitMonth/TwoDigitDay
or YYYY/MM/DD
, you make a function for it:
String format2(Date d) {
final f = d.formatter;
return '${f.yyyy}/${f.mm}/${f.dd}';
}
Then use it like before.
Note that formatter formats digits in English so if you want Persian digits you can use fonts with Persian digits or apply a simple mapping to formatter output to change English digits to Persian.
Jalali and Georgian dates support toString()
method. For Jalali, it is semantically equivalent to use a formatter as Jalali(Y,M,D)
which means:
String toStringFormatter(Jalali d) {
final f = d.formatter;
return 'Jalali(${f.y},${f.m},${f.d})';
}
For Georgian, toString() is equivalent to using a formatter as Georgian(Y,M,D)
.
Note: in the following code toString() is called implicitly:
void main() {
print(Jalali.now());
final str = 'today is: ${Georgian.now()}';
}
Use toString() of Jalali and Georgian dates only for development purpose, like for debugging, logging or ... You should use formatters for showing dates on the UI.
Note also that you do not need for example to use int.parse()
on formatter output of Jalali.now().formatter.m
for accessing its month, simply use Jalali.now().month
.
DateFormatter has these getters:
You can get date formatter by using formatter
getter on Jalali and Gregorian date objects. Simply cash this formatter in a Jalali value and then use string interpolation (as we have shown in examples) for making your desired output. This way of formatting is more powerful (and arguably easier) than using templates.
Jalali and Gregorian classes are Comparable so you can compare them using compareTo
method. You can also use comparison operators to compare them. They also support equals
and hashCode
functions. So you can safely use Sets and Maps of Jalali and Gregorian dates.
Jalali j1 = Jalali(1397, 1, 1);
Jalali j2 = Jalali(1397, 2, 1);
bool b1 = j1 < j2; // b1 = true
bool b2 = j1 >= j2; // b2 = false
// using Comparable compareTo
bool b3 = j1.compareTo(j2) > 0; // b3 = false (means j1 > j2 is false)
bool b4 = j1.compareTo(j2) <= 0; // b4 = true (means j1 <= j2 is true)
bool b5 = j1 == j2; // b5 = false
bool b6 = j1 != j2; // b6 = true
Here is a complete example. If you did not find what you are looking for, you can check test/shamsi_date_test.dart
file which includes unit tests.
import 'package:shamsi_date/shamsi_date.dart';
import 'package:shamsi_date/extensions.dart';
void main() {
// Gregorian to Jalali conversion
Gregorian g1 = Gregorian(2013, 1, 10, 12, 56, 34, 585);
Jalali j1 = g1.toJalali();
print('$g1 == $j1');
// prints: Gregorian(2013,1,10) == Jalali(1391,10,21)
// you can write Jalali.fromGregorian(g1) instead of g1.toJalali()
// access year, month and day through getters
// for Jalali:
int j1y = j1.year; // j1y = 1391
int j1m = j1.month; // j1m = 10
int j1d = j1.day; // j1d = 21
int j1th = j1.hour; // j1th = 12
int j1tm = j1.minute; // j1tm = 56
int j1ts = j1.second; // j1ts = 34
int j1tms = j1.millisecond; // j1tms = 585
print('j1 is $j1y-$j1m-$j1d:$j1th-$j1tm-$j1ts-$j1tms');
// prints: j1 is 1397-10-21:12-56-34-585
// NOTE: use formatters for formatting dates
// and for Gregorian:
int g1y = g1.year; // g1y = 2013
int g1m = g1.month; // g1m = 1
int g1d = g1.day; // g1d = 10
print('g1 is $g1y-$g1m-$g1d'); // prints: g1 is 2013-1-10
// NOTE: use formatters for formatting dates
// Jalali to Gregorian conversion
Jalali j2 = Jalali(1391, 10, 21);
Gregorian g2 = j1.toGregorian();
print('$j2 == $g2');
// prints: Jalali(1391,10,21) == Gregorian(2013,1,10)
// also can use Gregorian.fromJalali(j1) instead of j1.toGregorian()
// find weekDay
print('$j1 has weekDay ${j1.weekDay}'); // -> 6
// 6 means "پنج شنیه"
print('$g1 has weekDay ${g1.weekDay}'); // -> 4
// 4 means "Thursday"
// find month length
print('Jalali 1390/12 month length? '
'${Jalali(1390, 12).monthLength}'); // -> 29
print('Gregorian 2000/2 month length? '
'${Gregorian(2000, 2).monthLength}'); // -> 29
// check leap year
print('1390 Jalali is leap year? '
'${Jalali(1390).isLeapYear()}'); // -> false
print('2000 Gregorian is leap year? '
'${Gregorian(2000).isLeapYear()}'); // -> true
// validity:
// ALL created instances are considered VALID
// if you think a date might invalid, use try-catch:
try {
Jalali jv = Jalali(1398, 13, 1); // not valid!
print(jv); // this line is not reached
} on DateException catch (e) {
// prints: DateException: Jalali month is out of valid range.
print(e);
}
// making leap crash will also throw exception:
// for ex: Jalali(1394, 12, 30) will crash, since
// 1394 is not leap year
// creating dates out of computable range also throws DateException.
// convert DateTime object to Jalali and Gregorian
DateTime dateTime = DateTime.now();
print('now is $dateTime');
print('now is ${Gregorian.fromDateTime(dateTime)} in Gregorian');
print('now is ${Jalali.fromDateTime(dateTime)} in Jalali');
// convert to DateTime
print('$j1 is ${j1.toDateTime()}');
print('$g1 is ${g1.toDateTime()}');
// convert Jalali and Gregorian to DateTime
print('$j1 as DateTime is ${j1.toDateTime()}');
print('$g1 as DateTime is ${g1.toDateTime()}');
// find today with now() factory method
print('now is ${Gregorian.now()} in Gregorian');
print('now is ${Jalali.now()} in Jalali');
// find out which jalali year is this year:
int thisJalaliYear = Jalali.now().year;
print('this Jalali year is $thisJalaliYear');
// copy method
print('$j1 with year = 1300 is ${j1.copy(year: 1300)}');
print('$j1 with hour = 23 is ${j1.copy(hour: 23)}');
// prints: 1391/10/21 with year = 1300 is 1300/10/21
print('$g1 with month = 1 and day = 2 is ${g1.copy(month: 1, day: 2)}');
// prints: 2013/1/10 with month = 1 and day = 2 is 2013/1/2
// withYear, withMonth and withDay methods:
// these methods can be chained
// it is recommended to use these methods over copy method
print('$j1 with year = 1300 is ${j1.withYear(1300)}');
// prints: 1391/10/21 with year = 1300 is 1300/10/21
print('$g1 with month = 1 and day = 2 is ${g1.withDay(2).withMonth(1)}');
// prints: 2013/1/10 with month = 1 and day = 2 is 2013/1/2
// for example for getting date at start of this month in Jalali:
print(Jalali.now().copy(day: 1));
// for example to find 3rd day of 2nd month of this year:
print(Jalali.now().copy(month: 2, day: 3));
// DON NOT do it like this:
print(Jalali(Jalali.now().year, Jalali.now().month, 1)); // INCORRECT
// for example if you want to get
// the last day of the last month of this Jalali year:
Jalali tmp = Jalali.now().copy(month: 12, day: 1);
// since we can be in a leap year we use monthLength:
print(tmp.copy(day: tmp.monthLength));
// add and subtract days
Jalali d1 = Jalali(1398, 8, 4);
// add days
print(d1 + 3); // -> 1398/8/7
// result will be manipulated to become valid:
print(d1 + 30); // -> 1398/9/4
print(d1 + 365); // -> 1399/8/4
// subtract days
print(d1 - 2); // -> 1398/8/2
// add years, months and days:
print(d1.add(years: 1, months: 2, days: 3)); // 1399/10/7
// add years and days only:
print(d1.add(years: 1, days: 3)); // 1399/8/7
// add months only:
print(d1.add(months: 2)); // 1398/10/3
// add hours and minutes:
print(d1.add(hours: 1, minutes: 30)); // 1398/10/3
// if you want to subtract you can add negative value:
print(d1.add(years: -1)); // 1397/8/3
// and also for Gregorian
// you can find distance between two days with "^" operator
int distance11 = Jalali.now() ^ Jalali(1395, 10);
int distance12 = Jalali.now().distanceFrom(Jalali(1395, 10));
int distance13 = Jalali(1395, 10).distanceTo(Jalali.now());
print('distance $distance11 $distance12 $distance13');
// and similarly for Gregorian
// or you can use addYears, addMonths and addDays method
// it is recommended to use these methods over add method
// these methods are bound valid which means result will be
// manipulated to become valid, but add method is not
print(d1.addDays(30)); // -> 1398/9/4
print(d1.addDays(365)); // -> 1399/8/4
print(d1.addYears(1).addMonths(2).addDays(3)); // 1399/10/7
print(d1.addYears(1).addDays(3)); // 1399/8/7
print(d1.addMonths(2)); // 1398/10/3
print(d1.addYears(-1)); // 1397/8/3
// formatting examples:
// example one:
String format1(Date d) {
final f = d.formatter;
return '${f.wN} ${f.d} ${f.mN} ${f.yy}';
}
print(format1(j1)); // prints: پنج شنبه 21 دی 91
print(format1(g1)); // prints: Thursday 10 January 13
// example one:
String format2(Date d) {
final f = d.formatter;
return '${f.dd}/${f.mm}/${f.yyyy}';
}
print(format2(j1)); // prints: 21/10/1391
print(format2(g1)); // prints: 10/01/2013
// DO NOT use formatter for accessing year, month or other properties
// of date objects they are available as getters on date objects
// INCORRECT EXAMPLE, DO NOT USE THIS:
int j1y1 = int.parse(j1.formatter.yyyy); // INCORRECT
print("j1's year is $j1y1");
// use this:
int j1y2 = j1.year; // correct
print("j1's year is $j1y2");
// also using toString() for showing dates on UI is not recommended,
// use custom formatter.
// comparing dates examples:
print(j1 > j2); // -> false
print(j1.compareTo(j2) > 0); // -> false
print(j1 <= j2); // -> true
print(j1.compareTo(j2) <= 0); // -> true
print(g1 >= g2); // -> true
print(g1.compareTo(g2)); // -> 0
print(g1 == g2); // -> true
print(g1 != g1); // -> false
// if you want to compare Jalali with Georgian
// you can convert one type to another,
// for example:
print(j1.toGregorian() == g1); // -> true
// but if you don't want to convert them you can use julianDayNumber
// (this approach is not recommended)
print(j1.julianDayNumber == g1.julianDayNumber); // -> true
// this means that they are equal
// you can also use other comparison operators
// you can use extension methods for DateTime
final dtn = DateTime.now();
print(dtn);
final jn = dtn.toJalali();
print(jn);
final gn = dtn.toGregorian();
print(gn);
}
Run this command:
With Dart:
$ dart pub add shamsi_date
With Flutter:
$ flutter pub add shamsi_date
This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get
):
dependencies:
shamsi_date: ^1.0.1
Alternatively, your editor might support dart pub get
or flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:shamsi_date/shamsi_date.dart';
example/shamsi_date_example.dart
import 'package:shamsi_date/shamsi_date.dart';
void main() {
// Gregorian to Jalali conversion
Gregorian g1 = Gregorian(2013, 1, 10, 12, 56, 34, 585);
Jalali j1 = g1.toJalali();
print('$g1 == $j1');
// prints: Gregorian(2013,1,10) == Jalali(1391,10,21)
// you can write Jalali.fromGregorian(g1) instead of g1.toJalali()
// access year, month and day through getters
// for Jalali:
int j1y = j1.year; // j1y = 1391
int j1m = j1.month; // j1m = 10
int j1d = j1.day; // j1d = 21
int j1th = j1.hour; // j1th = 12
int j1tm = j1.minute; // j1tm = 56
int j1ts = j1.second; // j1ts = 34
int j1tms = j1.millisecond; // j1tms = 585
print('j1 is $j1y-$j1m-$j1d:$j1th-$j1tm-$j1ts-$j1tms');
// prints: j1 is 1397-10-21:12-56-34-585
// NOTE: use formatters for formatting dates
// and for Gregorian:
int g1y = g1.year; // g1y = 2013
int g1m = g1.month; // g1m = 1
int g1d = g1.day; // g1d = 10
print('g1 is $g1y-$g1m-$g1d'); // prints: g1 is 2013-1-10
// NOTE: use formatters for formatting dates
// Jalali to Gregorian conversion
Jalali j2 = Jalali(1391, 10, 21);
Gregorian g2 = j1.toGregorian();
print('$j2 == $g2');
// prints: Jalali(1391,10,21) == Gregorian(2013,1,10)
// also can use Gregorian.fromJalali(j1) instead of j1.toGregorian()
// find weekDay
print('$j1 has weekDay ${j1.weekDay}'); // -> 6
// 6 means "پنج شنیه"
print('$g1 has weekDay ${g1.weekDay}'); // -> 4
// 4 means "Thursday"
// find month length
print('Jalali 1390/12 month length? '
'${Jalali(1390, 12).monthLength}'); // -> 29
print('Gregorian 2000/2 month length? '
'${Gregorian(2000, 2).monthLength}'); // -> 29
// check leap year
print('1390 Jalali is leap year? '
'${Jalali(1390).isLeapYear()}'); // -> false
print('2000 Gregorian is leap year? '
'${Gregorian(2000).isLeapYear()}'); // -> true
// validity:
// ALL created instances are considered VALID
// if you think a date might invalid, use try-catch:
try {
Jalali jv = Jalali(1398, 13, 1); // not valid!
print(jv); // this line is not reached
} on DateException catch (e) {
// prints: DateException: Jalali month is out of valid range.
print(e);
}
// making leap crash will also throw exception:
// for ex: Jalali(1394, 12, 30) will crash, since
// 1394 is not leap year
// creating dates out of computable range also throws DateException.
// convert DateTime object to Jalali and Gregorian
DateTime dateTime = DateTime.now();
print('now is $dateTime');
print('now is ${Gregorian.fromDateTime(dateTime)} in Gregorian');
print('now is ${Jalali.fromDateTime(dateTime)} in Jalali');
// convert to DateTime
print('$j1 is ${j1.toDateTime()}');
print('$g1 is ${g1.toDateTime()}');
// convert Jalali and Gregorian to DateTime
print('$j1 as DateTime is ${j1.toDateTime()}');
print('$g1 as DateTime is ${g1.toDateTime()}');
// find today with now() factory method
print('now is ${Gregorian.now()} in Gregorian');
print('now is ${Jalali.now()} in Jalali');
// find out which jalali year is this year:
int thisJalaliYear = Jalali.now().year;
print('this Jalali year is $thisJalaliYear');
// copy method
print('$j1 with year = 1300 is ${j1.copy(year: 1300)}');
print('$j1 with hour = 23 is ${j1.copy(hour: 23)}');
// prints: 1391/10/21 with year = 1300 is 1300/10/21
print('$g1 with month = 1 and day = 2 is ${g1.copy(month: 1, day: 2)}');
// prints: 2013/1/10 with month = 1 and day = 2 is 2013/1/2
// withYear, withMonth and withDay methods:
// these methods can be chained
// it is recommended to use these methods over copy method
print('$j1 with year = 1300 is ${j1.withYear(1300)}');
// prints: 1391/10/21 with year = 1300 is 1300/10/21
print('$g1 with month = 1 and day = 2 is ${g1.withDay(2).withMonth(1)}');
// prints: 2013/1/10 with month = 1 and day = 2 is 2013/1/2
// for example for getting date at start of this month in Jalali:
print(Jalali.now().copy(day: 1));
// for example to find 3rd day of 2nd month of this year:
print(Jalali.now().copy(month: 2, day: 3));
// DON NOT do it like this:
print(Jalali(Jalali.now().year, Jalali.now().month, 1)); // INCORRECT
// for example if you want to get
// the last day of the last month of this Jalali year:
Jalali tmp = Jalali.now().copy(month: 12, day: 1);
// since we can be in a leap year we use monthLength:
print(tmp.copy(day: tmp.monthLength));
// add and subtract days
Jalali d1 = Jalali(1398, 8, 4);
// add days
print(d1 + 3); // -> 1398/8/7
// result will be manipulated to become valid:
print(d1 + 30); // -> 1398/9/4
print(d1 + 365); // -> 1399/8/4
// subtract days
print(d1 - 2); // -> 1398/8/2
// add years, months and days:
print(d1.add(years: 1, months: 2, days: 3)); // 1399/10/7
// add years and days only:
print(d1.add(years: 1, days: 3)); // 1399/8/7
// add months only:
print(d1.add(months: 2)); // 1398/10/3
// add hours and minutes:
print(d1.add(hours: 1, minutes: 30)); // 1398/10/3
// if you want to subtract you can add negative value:
print(d1.add(years: -1)); // 1397/8/3
// and also for Gregorian
// you can find distance between two days with "^" operator
int distance11 = Jalali.now() ^ Jalali(1395, 10);
int distance12 = Jalali.now().distanceFrom(Jalali(1395, 10));
int distance13 = Jalali(1395, 10).distanceTo(Jalali.now());
print('distance $distance11 $distance12 $distance13');
// and similarly for Gregorian
// or you can use addYears, addMonths and addDays method
// it is recommended to use these methods over add method
// these methods are bound valid which means result will be
// manipulated to become valid, but add method is not
print(d1.addDays(30)); // -> 1398/9/4
print(d1.addDays(365)); // -> 1399/8/4
print(d1.addYears(1).addMonths(2).addDays(3)); // 1399/10/7
print(d1.addYears(1).addDays(3)); // 1399/8/7
print(d1.addMonths(2)); // 1398/10/3
print(d1.addYears(-1)); // 1397/8/3
// formatting examples:
// example one:
String format1(Date d) {
final f = d.formatter;
return '${f.wN} ${f.d} ${f.mN} ${f.yy}';
}
print(format1(j1)); // prints: پنج شنبه 21 دی 91
print(format1(g1)); // prints: Thursday 10 January 13
// example one:
String format2(Date d) {
final f = d.formatter;
return '${f.dd}/${f.mm}/${f.yyyy}';
}
print(format2(j1)); // prints: 21/10/1391
print(format2(g1)); // prints: 10/01/2013
// DO NOT use formatter for accessing year, month or other properties
// of date objects they are available as getters on date objects
// INCORRECT EXAMPLE, DO NOT USE THIS:
int j1y1 = int.parse(j1.formatter.yyyy); // INCORRECT
print("j1's year is $j1y1");
// use this:
int j1y2 = j1.year; // correct
print("j1's year is $j1y2");
// also using toString() for showing dates on UI is not recommended,
// use custom formatter.
// comparing dates examples:
print(j1 > j2); // -> false
print(j1.compareTo(j2) > 0); // -> false
print(j1 <= j2); // -> true
print(j1.compareTo(j2) <= 0); // -> true
print(g1 >= g2); // -> true
print(g1.compareTo(g2)); // -> 0
print(g1 == g2); // -> true
print(g1 != g1); // -> false
// if you want to compare Jalali with Georgian
// you can convert one type to another,
// for example:
print(j1.toGregorian() == g1); // -> true
// but if you don't want to convert them you can use julianDayNumber
// (this approach is not recommended)
print(j1.julianDayNumber == g1.julianDayNumber); // -> true
// this means that they are equal
// you can also use other comparison operators
// you can use extension methods for DateTime
final dtn = DateTime.now();
print(dtn);
final jn = dtn.toJalali();
print(jn);
final gn = dtn.toGregorian();
print(gn);
}
Download details:
Author: FatulM
Source: https://github.com/FatulM/shamsi_date
1686122930
Learn how to replace a duplicated component by build a custom widget in Flutter. Building custom widgets in Flutter promotes code reusability, maintainability, consistency, abstraction, flexibility, and community collaboration.
Flutter has been getting more and more popular lately. You can use it to build complex applications that work smoothly on MacOS, Windows, and Linux.
But building these applications is not always a simple process. You often have to refactor your code to maintain the app’s performance.
One such refactoring technique is extracting duplicated code and components and reusing them in multiple places.
In this tutorial, you'll learn how to replace a duplicated component by building a custom widget in Flutter.
In Flutter, a custom widget refers to a user-defined widget that encapsulates a specific set of functionalities or visual representations.
Custom widgets are the building blocks of a Flutter application. They allow developers to create reusable UI components that can be used throughout the application.
If you're switching from React Native, you can think about custom widgets as custom React components. And what we call props
in React are called parameters
in Flutter.
Custom widgets help you encapsulate complex UI elements. They also promote code re-usability and enhance the maintainability of your Flutter applications.
There are a number of reasons to build build custom widgets in Flutter. Let's look at some of them.
Custom widgets allow developers to encapsulate complex functionality and appearance into reusable components.
Once created, custom widgets can be used multiple times throughout the application, reducing code duplication and promoting a modular development approach.
Custom widgets contribute to the maintainability of the codebase. By encapsulating specific functionality or visual representation, custom widgets create a separation of concerns. This separation makes it easier to locate, modify, and debug code related to a particular UI component.
They also enable developers to define a consistent and unified UI design across their application.
And finally, custom widgets provide a level of abstraction that hides the implementation details and complexity of a particular UI element.
You can create high-level widgets that expose a simplified interface and handle the internal logic. This allows other developers to use the widget without worrying about its internal workings. This abstraction promotes modularity, making it easier to understand, test, and maintain the code.
Let’s start building our custom widget.
Instead of starting from the scratch, I’ve created a Flutter app in GitHub and added duplicated code/components in that repo. Let’s begin from there.
Pull the code from GitHub by running the below command:
git clone https://github.com/5minslearn/Flutter-Custom-Widget.git
or
git clone git@github.com:5minslearn/Flutter-Custom-Widget.git
Clone the repo
Clone the Flutter Custom Widget repo from GitHub
By default, it’ll be in the master
branch. I’m switching to a refactor
branch (you don’t need to) because I want you all to have a look at my initial and final code. The initial code will be in the master
branch and the final code will be in the refactor
branch.
Run the following command to install all the dependencies:
cd Flutter-Custom-Widget/
flutter pub get
Install app dependencies
Install Flutter dependencies
Open the repo in Visual Studio Code and spin up your emulator (you may connect your mobile device, too). Once your emulator is up and running, press F5
to run the app in the emulator.
Here’s the view of your app on the first run.
Initial app run screen
If you’ve come this far, that’s great.
Let’s look at the code. Open the lib/main.dart
file.
We have a MyApp
class called at the beginning. This in-turn calls the MyHomePage
class.
This is our code for the entire UI which is defined in _MyHomePageState
class:
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Welcome to Flutter Refactoring Tutorial',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20)),
const SizedBox(height: 16),
const Text('Press the below button to follow me on Twitter'),
ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Pressed Follow on Twitter button"),
duration: Duration(seconds: 1),
),
);
// Open Twitter app
},
child: const Text("Follow on Twitter"),
),
const SizedBox(height: 16),
const Text('Press the below button to follow me on Instagram'),
ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Pressed Follow on Instagram button"),
duration: Duration(seconds: 1),
),
);
// Open Instagram app
},
child: const Text("Follow on Instagram"),
),
],
),
),
);
}
}
Code for the app UI
And so you can reference the line numbers, here's a visual:
Code for the app UI
If you’re someone who loves writing clean code, you would definitely say that this is ugly code.
Here’s the reason for it. Look at the code carefully – lines 44 to 56 and lines 58 to 70 are completely duplicated except for a very few handpicked words. For example, the word “Twitter” has been replaced with the word “Instagram”.
The clean coder will definitely refactor this code before working on adding new features/functionalities. Let's follow those clean coding practices now, too.
We have to extract the text and button into a separate component. This component should accept the platform
and onPressed
as its parameters. We can template out the common text from them.
So, our code to build the custom widget looks like this:
class CustomButton extends StatelessWidget {
final String platform;
final VoidCallback onPressed;
const CustomButton(
{super.key, required this.platform, required this.onPressed});
@override
Widget build(BuildContext context) {
return Center(
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Text("Press the below button to follow me on $platform"),
ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("Pressed Follow on $platform button"),
duration: const Duration(seconds: 1),
),
);
onPressed();
},
child: Text("Follow on $platform"),
)
]));
}
}
Create a custom widget
As we discussed above, the template text and accept platform
and onPressed
parameters. We replaced platform
wherever we need and call the onPressed
method as the extension of showing a snack bar.
Add the above code at the very end of the main.dart
file.
Let’s integrate our custom widget into our code.
Pick the first block of code from the line 44 to 56 as shown below
const Text('Press the below button to follow me on Twitter'),
ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Pressed Follow on Twitter button"),
duration: Duration(seconds: 1),
),
);
// Open Twitter app
},
child: const Text("Follow on Twitter"),
),
Refactor the first block of code
Replace it with the following code:
CustomButton(
platform: 'Twitter',
onPressed: () {
// Open Twitter App
},
),
Use our custom widget for Twitter button
Similarly, pick the next block of code from the line 58 to 70 as shown below
const Text('Press the below button to follow me on Instagram'),
ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Pressed Follow on Instagram button"),
duration: Duration(seconds: 1),
),
);
// Open Instagram app
},
child: const Text("Follow on Instagram"),
),
Refactor second block of code
Replace it with the following code:
CustomButton(
platform: 'Instagram',
onPressed: () {
// Open Instagram App
},
),
Use our custom widget for Instagram button
Here's the final code of _MyHomePageState
class after we complete our refactoring process.
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Welcome to Flutter Refactoring Tutorial',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20)),
const SizedBox(height: 16),
CustomButton(
platform: 'Twitter',
onPressed: () {
// Open Twitter App
},
),
const SizedBox(height: 16),
CustomButton(
platform: 'Instagram',
onPressed: () {
// Open Instagram App
},
),
],
),
),
);
}
}
After refactoring your code
And again, here's the screenshot for line number reference:
After refactoring your code
Run your app now.
Unfortunately, you’ll not notice any change in the UI. But your underlying code has changed. That’s exactly what refactoring is.
Quoting from Martin Fowler,
Refactoring is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior. – https://refactoring.com/
Final app
You may be wondering something after looking at the above code. Lines 43 and 50 also contain the same code (const SizedBox(height: 16),
). So why don’t we include that into the component?
That’s great if you had this question.
There is no need for the custom widget component to include the SizedBox
component. This is because the SizedBox
component is added in the Home page to give some space between each component. But it's not necessary that whenever we use this button, we give a space at the top/bottom of the widget.
Still, if such cases arise, you can add the SizedBox
widget inside your custom widget.
You might not see a direct benefit right away. But you may experience it in the future. Here’s a quick example for you.
Let’s assume you’ve built this app for a client. It has become a complex app and you’ve used this custom widget around 20 places in your app. The app is released and people enjoy using it.
About 6 months later, your client come back to you with the next version of changes. One of the items in the huge list is, “We’re coming up with a slight change in theme. Replace all the social media referral buttons so that they're an outlined shape and change the color to green”.
It is one simple configuration change in the custom widget. But imagine if you hadn't built the custom widget and had to copy/pasted the same code in all the 20 places. Then you'd have to carefully look at each place and replace each instance with care without touching other pieces of code.
These are the only 2 lines we have to change in our custom widget in this example:
OutlinedButton(
style: OutlinedButton.styleFrom(foregroundColor: Colors.green),
Code change to be done on custom widget for the above requirement
Changes in our custom widget
But if you hadn't refactored your code, you'd have to make this change in 20 places.
Small change reflects everywhere
I’ve pushed my code to the same GitHub repo. Refer to the master
branch for the non-refactored code and the refactor
branch for the refactored code.
Always use custom widgets for their specific use cases. For example, in our case, it is for Social media redirects. This widget should not be used in places which are unrelated to its context.
If you do, remember the above case where the client requirement was to change the design of only the social media referral buttons...but our change would be applied to all the other places where this widget was used. This would lead to unexpected bugs.
You should always write unit test cases for Custom Widgets which will help you mitigate any bugs earlier.
One more tip is to name your component in a more readable way. This helps other developers know what the widget does just by reading its name.
In our case, I've named it CustomButton
which makes no sense. Instead, some good alternatives would be SocialMediaButton
, SocialButton
, and so on which fit into our use case.
In this tutorial, you learned about building a custom widget by removing duplicated code/components.
Building custom widgets in Flutter promotes code reusability, maintainability, consistency, abstraction, flexibility, and community collaboration.
Custom widgets are a powerful tool in the Flutter developer’s toolkit, enabling you to create beautiful and functional user interfaces while maximizing efficiency and maintainability.
Source: https://www.freecodecamp.org
#flutter #dart
1686106385
Flutter 3.3 app development tutorial master class for beginners to advanced course 2023. This ticket booking app covers Flutter for very beginners to advanced learners. We start from very simple ui like drawing text to complex layout using stack and mixture of column and row widget.
0:00:00 Booking App intro
0:00:38 Introduction of the Booking App
0:01:51 Creating the Project Book ticket
0:03:17 Project Structure and functions explained
0:06:00 functions of main.dart as an entry point explained
0:09:45 functions of setState ((){}) explained
0:12:20 create and setup new class BottomBar.dart in screens folder
0:13:39 create stateful class widget called BottomBar
0:14:59 running the app on emulator
0:15:39 use the scaffold widget to make your screen colorful
0:17:15 functions of the MaterialApp widget explained
0:18:42 function of the body property in scaffold widget explained
0:25:30 run ap on emulator
0:28:24 add more properties to bottomNavigationBar
0:33:35 restart app on emulator
0:46:49 Restart App on emulator to reflet changes
0:51:50 Row and Column layout explained
0:54:00 Add Text widget to Row
0:55:40 Add image asset to Row widget
0:57:21 Add asset folder path to pubspec.yml file
1:00:44 create a container and apply decoration and set height and width dimension
1:04:52 wrap text widget with column widget
1:10:23 Rename AppBar widget in Bottom_bar file and restart app
1:20:49 Apply style to background color and text in home_screen.dart file
1:23:50 Add gap dependency to upbspec.yml file
1:26:00 Create a new Row layout widget
1:29:03 Wrap Row widget with Container, add padding and decoration property
1:33:12 Add Row widget and apply text widget and style property
1:35:15 Wrap text widget with inkwell widget and add ontap function
1:37:24 create a new file ticket_view.dart
1:41:50 Add container with column widget in SizedBox in TicketView class
1:48:44 Apply decoration property to container
1:58:20 Add expanded widget and use flex as child property
2:04:10 Add transfom-rotate widget and add necessary parameters
2:21:59 Wrap layoutBuilder widget with padding widget
2:30:28 Wrap ticketView widget in main_screen.dart with SingleChildScrollView widget
2:39:35 Add image to container widget
2:42:20 Wrap HotelScreen widget with SingleChildScrollView widget
3:03:10 Run app on emulator
3:10:25 Add ticket map to constructors in TicketView class
3:17:38 Add Get dependency to pubspec.yml file
3:41:20 Work on SearchScreen class to create hotels tab
3:50:50 Call AppIconText widget in SearchScreen class
4:16:36 wrap container widget with stack widget
4:26:04 create a file ticket_tabs.dart in widget folder and create a stateless class AppTicketTabs
4:34:30 work on ticket_screen.dart
4:53:41 make changes to text style
4:54:09 Add background color to Scaffold widget
4:57:18 Add color:colors.white to container widget
4:58:50 work on height variations ratio in IOS and android
5:04:16 Restart App in emulator
5:04:45 Apply mainAxisAlignment to Row widget
5:11:56 Make changes to column_layout.dart file
5:17:00 work on ticket_screen.dart
5:21:58 work on ticket_screen.dart file
5:38:57 wrap Barcode widget around clipRect widget
5:47:20 work on ticket_screen.dart file
5:51:25 wrap container around Positioned widget
5:56:15 Add ProfileScreen widget to BottomBar and refresh App in emulator
5:57:00 work on Profile_screen file
6:01:49 Add crossAxisAlignment property to column widget
6:04:17 Add Row widget to container
6:08:42 Apply padding to container
6:12:45 Add stack widget to children property and apply necessary parameter
6:16:48 Add row widget with children property and add necessary parameter
6:19:00 Add Text widget to column property and apply style to text
6:26:00 Add container with column widget and apply Text widget
6:32:35 Apply alignment to Row widget
6:34:58 Add AppLayoutBuilder widget and apply necessary parameter
6:39:10 Apply padding and decoration to container widget
6:42:04 Add Text widget and apply styling property
More about it
https://www.dbestech.com/tutorials/flutter-ticket-booking-app
Subscribe: https://www.youtube.com/@dbestech/featured
1686065055
Firebase UI for Firestore
Firebase UI for Firestore enables you to easily integrate your application UI with your Cloud Firestore database.
flutter pub add cloud_firestore
flutter pub add firebase_ui_firestore
Import the Firebase UI for Firestore package:
import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';
Infinite scrolling is the concept of continuously loading more data from a database as the user scrolls through your application. This is useful when you have a large datasets, as it enables the application to render faster as well as reduces network overhead for data the user might never see.
Firebase UI for Firestore provides a convenient way to implement infinite scrolling using the Firestore database with the FirestoreListView
widget.
At a minimum the widget accepts a Firestore query and an item builder. As the user scrolls down (or across) your list, more data will be automatically fetched from the database (whilst respecting query conditions such as ordering).
To get started, create a query and provide an item builder. For this example, we'll display a list of users from the users
collection:
final usersQuery = FirebaseFirestore.instance.collection('users').orderBy('name');
FirestoreListView<Map<String, dynamic>>(
query: usersQuery,
itemBuilder: (context, snapshot) {
Map<String, dynamic> user = snapshot.data();
return Text('User name is ${user['name']}');
},
);
The FirestoreListView
widget is built on-top of Flutter's own ListView
widget, and accepts the same parameters which we can optionally provide. For example, to change the scroll-direction to horizontal:
FirestoreListView<Map<String, dynamic>>(
scrollDirection: Axis.horizontal,
// ...
);
By default, the widget will fetch 10 items from the collection at a time. This can be changed by providing a pageSize
parameter:
FirestoreListView<Map<String, dynamic>>(
pageSize: 20,
// ...
);
In general, it is good practice to keep this value as small as possible to reduce network overhead. If the height (or width) of an individual item is large, it is recommended to lower the page size.
The cloud_firestore
plugin allows us to type the responses we receive from the database using the withConverter
API. For more information, see the documentation.
The FirestoreListView
works with this out of the box. Simply provide a converted query to the widget, for example:
class User {
User({required this.name, required this.age});
User.fromJson(Map<String, Object?> json)
: this(
name: json['name']! as String,
age: json['age']! as int,
);
final String name;
final int age;
Map<String, Object?> toJson() {
return {
'name': name,
'age': age,
};
}
}
final usersQuery = FirebaseFirestore.instance.collection('users')
.orderBy('name')
.withConverter<User>(
fromFirestore: (snapshot, _) => User.fromJson(snapshot.data()!),
toFirestore: (user, _) => user.toJson(),
);
FirestoreListView<User>(
query: usersQuery,
itemBuilder: (context, snapshot) {
// Data is now typed!
User user = snapshot.data();
return Text(user.name);
},
);
By default, the widget will display a loading indicator while data is being fetched from the database, and ignore any errors which might be thrown (such as permission denied). You can override this behavior by providing a loadingBuilder
and errorBuilder
parameters to the widget:
FirestoreListView<Map<String, dynamic>>(
loadingBuilder: (context) => MyCustomLoadingIndicator(),
errorBuilder: (context, error, stackTrace) => MyCustomError(error, stackTrace),
// ...
);
In many cases, the FirestoreListView
widget is enough to render simple lists of collection data. However, you may have specific requirements which require more control over the widget's behavior (such as using a GridView
).
The FirestoreQueryBuilder
provides the building blocks for advanced configuration at the expense of requiring more boilerplate code. The widget does not provide any underlying list implementation, instead you are expected to provide this yourself.
Much like the FirestoreListView
widget, provide a query and builder:
final usersQuery = FirebaseFirestore.instance.collection('users').orderBy('name');
FirestoreQueryBuilder<Map<String, dynamic>>(
query: usersQuery,
builder: (context, snapshot, _) {
// ... TODO!
},
);
The main difference to note here is that the builder
property returns a QueryBuilderSnapshot
, rather than an individual document. The builder returns the current state of the entire query, such as whether data is loading, an error has occurred and the documents.
This requires us to implement our own list based implementation. Firstly, let's handle the loading and error states:
FirestoreQueryBuilder<Map<String, dynamic>>(
query: usersQuery,
builder: (context, snapshot, _) {
if (snapshot.isFetching) {
return const CircularProgressIndicator();
}
if (snapshot.hasError) {
return Text('Something went wrong! ${snapshot.error}');
}
// ...
},
);
Next, we now need to return a list-view based implementation for our application to display the data. For example, to display a grid of users, we can use the GridView
widget:
FirestoreQueryBuilder<Map<String, dynamic>>(
query: usersQuery,
builder: (context, snapshot, _) {
// ...
return GridView.builder(
itemCount: snapshot.docs.length,
itemBuilder: (context, index) {
// if we reached the end of the currently obtained items, we try to
// obtain more items
if (snapshot.hasMore && index + 1 == snapshot.docs.length) {
// Tell FirestoreQueryBuilder to try to obtain more items.
// It is safe to call this function from within the build method.
snapshot.fetchMore();
}
final user = snapshot.docs[index].data();
return Container(
padding: const EdgeInsets.all(8),
color: Colors.teal[100],
child: const Text("User name is ${user['name']}"),
);
},
);
},
);
With more power comes more responsibility:
itemBuilder
of our GridView
, we have to manually ensure that we call the fetchMore()
method on the snapshot when more data is required.FirestoreQueryBuilder
does not provide a list-view based handler, instead you must provide your own implementation.Run this command:
With Flutter:
$ flutter pub add firebase_ui_firestore
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
firebase_ui_firestore: ^1.5.2
Alternatively, your editor might support flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';
// Copyright 2022, the Chromium project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';
import 'package:firebase_ui_firestore_example/firebase_options.dart';
import 'package:flutter/material.dart';
late CollectionReference<User> collection;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
final collectionRef = FirebaseFirestore.instance.collection('users');
collection = collectionRef.withConverter<User>(
fromFirestore: (snapshot, _) => User.fromJson(snapshot.data()!),
toFirestore: (user, _) => user.toJson(),
);
runApp(const FirebaseUIFirestoreExample());
}
class FirebaseUIFirestoreExample extends StatelessWidget {
const FirebaseUIFirestoreExample({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Contacts')),
body: FirestoreListView<User>(
query: collection,
padding: const EdgeInsets.all(8.0),
itemBuilder: (context, snapshot) {
final user = snapshot.data();
return Column(
children: [
UserTile(user: user),
const Divider(),
],
);
},
),
),
);
}
}
class UserTile extends StatelessWidget {
final User user;
const UserTile({
super.key,
required this.user,
});
@override
Widget build(BuildContext context) {
return Row(
children: [
CircleAvatar(
child: Text(user.firstName[0]),
),
const SizedBox(width: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Text(
'${user.firstName} ${user.lastName}',
style: Theme.of(context).textTheme.titleMedium,
),
Text(
user.number,
style: Theme.of(context).textTheme.bodySmall,
),
],
),
],
);
}
}
class User {
User({
required this.city,
required this.country,
required this.streetName,
required this.zipCode,
required this.prefix,
required this.firstName,
required this.lastName,
required this.email,
required this.userName,
required this.number,
});
User.fromJson(Map<String, Object?> json)
: this(
city: json['city'].toString(),
country: json['country'].toString(),
streetName: json['streetName'].toString(),
zipCode: json['zipCode'].toString(),
prefix: json['prefix'].toString(),
firstName: json['firstName'].toString(),
lastName: json['lastName'].toString(),
email: json['email'].toString(),
userName: json['userName'].toString(),
number: json['number'].toString(),
);
final String city;
final String country;
final String streetName;
final String zipCode;
final String prefix;
final String firstName;
final String lastName;
final String email;
final String userName;
final String number;
Map<String, Object?> toJson() {
return {
'city': city,
'country': country,
'streetName': streetName,
'zipCode': zipCode,
'prefix': prefix,
'firstName': firstName,
'lastName': lastName,
'email': email,
'userName': userName,
'number': number,
};
}
}
Download details:
Author: firebase.google.com
Source: https://github.com/firebase/flutterfire/tree/master/packages/firebase_ui_firestore
#flutter #android #web-development #web #dart #firebase #google #ui
1685923606
A cookie manager for http requests in Dart, help you to deal with the cookie policies and persistence.
Checkout the Migration Guide for breaking changes between versions.
You can use the command to add dio as a dependency with the latest stable version:
$ dart pub add cookie_jar
Or you can manually add cookie_jar into the dependencies section in your pubspec.yaml:
dependencies:
cookie_jar: ^replace-with-latest-version
A simple usage example:
import 'package:cookie_jar/cookie_jar.dart';
void main() async {
final cookieJar = CookieJar();
List<Cookie> cookies = [Cookie('name', 'wendux'), Cookie('location', 'china')];
// Saving cookies.
await cookieJar.saveFromResponse(Uri.parse('https://pub.dev/'), cookies);
// Obtain cookies.
List<Cookie> results = await cookieJar.loadForRequest(Uri.parse('https://pub.dev/paths'));
print(results);
}
SerializableCookie
This class is a wrapper for Cookie
class. Because the Cookie
class doesn't support Json serialization, for the sake of persistence, we use this class instead of it.
CookieJar
CookieJar
is a cookie container and manager for HTTP requests.
DefaultCookieJar
DefaultCookieJar
is a default cookie manager which implements the standard cookie policy declared in RFC. It saves the cookies in the memory, all cookies will be cleared after the app exited.
To use it:
final cookieJar = CookieJar();
PersistCookieJar
PersistCookieJar
is a cookie manager which implements the standard cookie policy declared in RFC. PersistCookieJar
persists the cookies in files, so if the application exit, the cookies always exist unless call delete
explicitly.
To use it:
// Cookie files will be saved in files in "./cookies/4/"
final cookieJar = PersistCookieJar(
ignoreExpires: true, // Save/load even cookies that have expired.
);
Note: When using the [FileStorage] in Flutter apps, use path_provider to obtain available directories.
Directory tempDir = await
getTemporaryDirectory();
final tempPath = tempDir.path;
final cookieJar = PersistCookieJar(
ignoreExpires: true,
storage: FileStorage(tempPath),
);
You can customize your own storage by extending Storage
, see FileStorage
for more details.
Future
Save the cookies for specified uri.
Future<List
Load the cookies for specified uri.
Future
Delete cookies for specified uri
. This API will delete all cookies for the uri.host
, it will ignore the uri.path
.
If withDomainSharedCookie
is true
, all domain-shared cookies will be deleted.
Future
Delete all cookies.
HttpClient
Using CookieJar
or PersistCookieJar
manages HttpClient
's request/response cookies is quite easy:
final cookieJar = CookieJar();
request = await httpClient.openUrl(options.method, uri);
request.cookies.addAll(await cj.loadForRequest(uri));
response = await request.close();
await cookieJar.saveFromResponse(uri, response.cookies);
dio is a powerful HTTP client for Dart/Flutter, which supports global configuration, interceptors, FormData, request cancellation, file uploading/downloading, timeout, and custom adapters etc. dio also supports to manage cookies with cookie_jar
using dio_cookie_manager. For example:
import 'package:dio/dio.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:cookie_jar/cookie_jar.dart';
void main() async {
final dio = Dio();
final cookieJar = CookieJar();
dio.interceptors.add(CookieManager(cookieJar));
await dio.get('https://pub.dev/');
// Print cookies
print(await cookieJar.loadForRequest(Uri.parse('https://pub.dev/')));
// Another request with the cookie.
await dio.get("https://pub.dev/");
}
More details about dio.
The project and it's underlying projects are originally authored by @wendux with the organization @flutterchina since 2018.
The project consents the MIT license.
Run this command:
With Dart:
$ dart pub add cookie_jar
With Flutter:
$ flutter pub add cookie_jar
This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get
):
dependencies:
cookie_jar: ^4.0.3
Alternatively, your editor might support dart pub get
or flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:cookie_jar/cookie_jar.dart';
example/cookie_jar_example.dart
import 'dart:io';
import 'package:cookie_jar/cookie_jar.dart';
void main() async {
final cookies = <Cookie>[
Cookie('name', 'wendux'),
Cookie('location', 'china'),
];
final cookiesExpired = <Cookie>[
Cookie('name', 'wendux')..maxAge = 1,
Cookie('location', 'china')
..expires = DateTime.now().add(const Duration(hours: 1)),
];
//final cj = CookieJar();
//final cj = PersistCookieJar();
final cj = PersistCookieJar(storage: FileStorage('./example/.cookies'));
await cj.saveFromResponse(Uri.parse('https://www.baidu.com/xx'), cookies);
List<Cookie> results = await cj.loadForRequest(Uri.parse('https://www.baidu.com/xx'));
assert(results.length == 2);
results = await cj.loadForRequest(Uri.parse('https://www.baidu.com/xx/dd'));
assert(results.length == 2);
results = await cj.loadForRequest(Uri.parse('https://www.baidu.com/'));
assert(results.isEmpty);
await cj.saveFromResponse(Uri.parse('https://google.com'), cookiesExpired);
results = await cj.loadForRequest(Uri.parse('https://google.com'));
assert(results.length == 2);
await Future<void>.delayed(const Duration(seconds: 2), () async {
results = await cj.loadForRequest(Uri.parse('https://google.com'));
assert(results.length == 1);
});
}
Download details:
Author: flutterchina.club
Source: https://github.com/flutterchina/cookie_jar
1685810988
A port of .NET's LINQ IEnumerable functions to Dart. This package extends the native Iterable
type with all of the LINQ methods that do not exist in native Dart. Starting with version 0.5, this package also contains the extension methods from the MoreLINQ .NET library.
Because this library uses Dart 2.6's new extension methods, any Iterable
has access to these methods as though they were native methods. This includes classes that extend from Iterable
, such as List
and Set
.
In addition, this library adds several new types of Iterable
classes to make some utility functions easier:
// Creates an iterable containing the numbers from 2 to 6: [2, 3, 4, 5, 6]
var rangeI = RangeIterable(2, 6, inclusive: true);
// Creates an iterable that contains 3 copies of the value 'abc': ['abc', 'abc', 'abc']
var repeatI = RepeatIterable('abc', 3);
// Creates an iterable from a string, iterating over its characters
// This is an extension getter property on String that returns an
// iterable via `String.codeUnits.map((u) => String.fromCodeUnit(u))`.
// Results in ['a', 'b', 'c', 'd', 'e', 'f']
var stringI = 'abcdef'.iterable;
// Same as above but using `runes` instead of `codeUnits` to respect
// rune boundaries and maintain surrogate pairs.
var stringIR = 'abcdef'.iterableRunes;
You can call any of 40 new methods on it to modify or analyze it. For example, the native method map
is expanded upon with select
, which combines the element with the index at which the element is found within the iterable:
var list = [10, 20, 30];
var mappedList = list.select((i, index) => '$index-$i'); // ['1-10', '2-20', '3-30']
There are "OrDefault" variants on several common iterator
value getter methods, such as firstOrDefault
, singleOrDefault
, and defaultIfEmpty
. Instead of throwing an error, these methods will return a default value (or null if left unspecified) if the element(s) cannot be found:
var list = <String>[];
var native = list.first; // Throws a StateError
var orDefault = list.firstOrDefault('abc'); // Returns 'abc'
var list2 = [1, 2, 3];
var importantValue = list2.where((i) => i >= 4)
.defaultIfEmpty(-1); // Returns [-1]
You can filter an iterable down to unique instances of elements with the distinct
method:
var list = [1, 1, 1, 2, 2, 3, 4, 5, 5, 5, 5, 5];
var uniqueList = myEnum.distinct(); // [1, 2, 3, 4, 5]
There are also set operations with the except
, intersect
, and union
methods:
var listA = [1, 2, 3, 4];
var listB = [3, 4, 5, 6];
var exclusion = listA.except(listB); // [1, 2]
var intersection = listA.intersect(listB); // [3, 4]
var union = listA.union(listB); // [1, 2, 3, 4, 5, 6]
And you can group elements together by common features using groupBy
:
var list = [1, 2, 3, 4, 5, 6];
var groupedList = list.groupBy((i) => i / 3 == 0); // [[1, 2, 4, 5], [3, 6]]
Or bundle them into groups of a fixed length using segment
:
var list = [1, 2, 3, 4, 5, 6];
var segmented = list.segment(2); // [[1, 2], [3, 4], [5, 6]]
You can even perform complex ordering functions using orderBy
and thenBy
:
var list = ['ab', 'a', 'c', 'aa', ''];
// Sort by string length followed by alphabetical order
var ordered = list.orderBy((c) => c.length)
.thenBy((c) => c);
// Result: ['', 'a', 'c', 'aa', 'ab']
Just like in native dart, every method returns a new Iterable
, so you can chain methods together to make complex mapping, grouping, and filtering behavior:
var list = [3, 1, 6, 2, 3, 2, 4, 1];
var result = list.select((i, idx) => i * 2 + idx) // [6, 3, 14, 8, 10, 10, 14, 9]
.distinct() // [6, 3, 14, 8, 10, 9]
.where((i) => i > 4) // [6, 14, 8, 10, 9]
.orderBy((i) => i) // [6, 8, 9, 10, 14]
.map((i) => i.toRadixString(16)); // [6, 8, 9, A, E]
NEW in 2.0.0
With record support added in Dart 3, A new method has been added called deconstruct
. It's purpose is to easily unwrap iterables of records, resulting in separate iterables containing the corresponding fields of the internal records.
var johns = [
("John Doe", 26, "E145326"),
("John Wick", 58, "E645091"),
("John Teesonter", 15, "E997123"),
];
var (names, ages, ids) = johns.deconstruct();
print(names); // ["John Doe", "John Wick", "John Teesonter"]
print(ages); // [26, 58, 15]
print(ids); // ["E145326", "E645091", "E997123"]
This extension method is implemented on lists containing records with up to nine fields.
(Note: Due to apparent current language restrictions, records containing named fields are not supported.)
As a necessity for some operations, I needed a Tuple
class, and as I was unsatisfied with the current offerings out there right now, I elected to create my own.
For the uninitiated, tuples are similar to lists in concept that they contain multiple values addressable by index. But where every element of a list must resolve to the same type (the type of the list), each element in a tuple can be its own specified type. This results in being able to contain, distribute, and access the items in a tuple in a type-safe way. You could, for example, use a Tuple2<double, String>
to return two values from a function and be able to access both the double
and the String
values without needing to resort to fragile methods such as dynamic
or runtime type casting. Another difference between lists and tuples is that tuples are inherently immutable, so they aren't susceptible to side effects stemming from mutation and can even benefit from being declared as constants.
This package exposes tuple classes from Tuple0
up to Tuple9
, depending on how many items the tuple contains. (Yes, I agree that Tuple0
and Tuple1
seem largely redundant, but I've seen them exist in the tuple libraries of many programming languages so it must serve some purpose or other, so I included them here all the same for completeness if nothing else.) Each tuple class includes the following features:
tuple.item2
) or by indexer(tuple[2]
). (Note that access by indexer is not type-safe)fromJson
and method toJson
means tuples are seralization-ready.fromList
to generate a tuple from a list (automatically casting when specifying type parameters for the constructor).asType
method allows freely casting the tuple from any assortment of types to any other assortment of types (provided the items are compatible with those types).asDynamic
convenience method for casting a tuple to dynamic items.copyWith
method allows easy generation of duplicate tuples, optionally specifying new values for specific items.copyWithout
method allows selective filtering of items from a tuple resulting in a lower-order tuple.mapActions
method allows you to iterate over each item with an exhaustive list of type-safe callbacks.Iterable<dynamic>
, so it can be treated as a normal iterable (and thus combined with any darq extension method).==
and hashCode
are both implemented, tuples can be directly compared for equality or used as keys for maps and other hash sets.As of version 0.5, this package also contains the extension methods from the MoreLINQ .NET library. This more than triples the available number of extension methods over vanilla LINQ.
Some examples of the new methods from MoreLINQ include:
index
:
var list = ['a', 'b', 'c'];
var indexedList = list.index();
// Iterable:
// [ [0, 'a'], [1, 'b'], [2, 'c'] ]
assertAll
:
var list = [2, 4, 5];
list.assertAll((x) => x.isEven);
// Throws an assertion error
awaitAll
:
var list = [
Future.delayed(Duration(seconds: 1)),
Future.delayed(Duration(seconds: 2)),
Future.delayed(Duration(seconds: 3)),
];
await list.awaitAll();
// Waits for 3 seconds before continuing
subsets
:
var list = [1, 2, 3];
var subsets = list.subsets();
// Iterable:
// [ [], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3] ]
interleave
:
var listA = [1, 3, 5];
var listB = [2, 4, 6];
var combined = listA.interleave(listB);
// Iterable:
// [1, 2, 3, 4, 5, 6]
permutations
:
var list = [1, 2, 3];
var perms = list.permutations();
// Iterable:
// [ [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1] ]
split
:
var list = ['a', ' ', 'b', 'c', ' ', 'd'];
var split = list.split(' ');
// Iterable:
// [ ['a'], ['b', 'c'], ['d'] ]
Run this command:
With Dart:
$ dart pub add darq
With Flutter:
$ flutter pub add darq
This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get
):
dependencies:
darq: ^2.0.0
Alternatively, your editor might support dart pub get
or flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:darq/darq.dart';
import 'package:darq/darq.dart';
void main() {
benchmark();
// sandbox();
}
void sandbox() {
final list = ['a', 'b', 'b', 'c'];
print(list.distinct((s) => s.hashCode).toList());
}
typedef Foo2<T1, T2> = T2 Function(T1 a);
typedef Foo3<T1, T2, T3> = T3 Function(T1 a, T2 b);
class Bar<A> {
final List<A> outer;
Bar(this.outer);
void run<B, C, D>(
List<B> inner,
Foo2<A, C> outerSelector,
Foo2<B, C> innerSelector,
Foo3<A, B, D> resultSelector,
) {
print([A, B, C, D]);
print([
outer.runtimeType,
inner.runtimeType,
outerSelector.runtimeType,
innerSelector.runtimeType,
resultSelector.runtimeType
]);
}
}
class Person {
final String name;
Person(this.name);
}
class Pet {
final String name;
final String owner;
Pet(this.name, this.owner);
}
void benchmark() {
final source = List.generate(1000000, (i) => i);
final iterations = 100;
final benchmarks = List<double>.generate(iterations, (_) => -1);
// LINQ style
for (var i = 0; i < iterations; i++) {
final start = DateTime.now();
// ======================BENCHMARK START=============================
final result = source.groupBy((i) => i % 3).select((g, i) => g.average());
result.consume();
// ======================BENCHMARK END===============================
final end = DateTime.now();
benchmarks[i] =
(end.microsecondsSinceEpoch - start.microsecondsSinceEpoch) / 1000000;
}
print('Average execution time in seconds (LINQ): ${benchmarks.average()}');
// Vanilla Style
for (var i = 0; i < iterations; i++) {
final start = DateTime.now();
// ======================BENCHMARK START=============================
final result = <List<int>>[[], [], []];
for (var i in source) {
result[i % 3].add(i);
}
for (var g in result) {
var total = 0;
for (var i in g) {
total += i;
}
final _ = total / g.length;
// Go something with the value
}
// ======================BENCHMARK END===============================
final end = DateTime.now();
benchmarks[i] =
(end.microsecondsSinceEpoch - start.microsecondsSinceEpoch) / 1000000;
}
print('Average execution time in seconds (Vanilla): ${benchmarks.average()}');
}
Download details:
Author: abion47
Source: https://github.com/abion47/darq
1685810741
This project is a port of Kotlin's Kotlin Standard library for Dart/Flutter projects. It's a useful addition to dart:core
and includes collections (KtList
, KtMap
, KtSet
) as well as other packages which can improve every Dart/Flutter app.
dependencies:
kt_dart: ^1.1.0
import 'package:kt_dart/kt.dart';
Dart's dart:core
package provides basic building blocks. But sometimes they are too low level and not as straightforward as Kotlin's kotlin-stdlib
.
Here are a few examples of what this project offers: (click to expand)
Immutable collections by default
Deep equals
Common methods
KtList
is a read-only list of elements. It is immutable because it doesn't offer mutation methods such as remove
or add
. Use KtMutableMap
if you want to use a mutable list.
To create a KtList
/KtMutableList
use the KtList.of
constructor or convert an existing Dart List
to a KtList
with the list.toImmutableList()
extension.
// Create a KtList from scratch
final beatles = KtList.of("John", "Paul", "George", "Ringo");
// Convert a existing List to KtList
final abba = ["Agnetha", "Björn", "Benny", "Anni-Frid"];
final immutableAbba = abba.toImmutableList();
KtList
is immutable by default, which means it doesn't offer methods like add
or remove
. To create mutable list with kt_dart use the KtMutableList
constructor.
// Create a KtMutableList from scratch
final beatles = KtMutableList.of("John", "Paul", "George", "Ringo");
beatles.removeAt(0);
print(beatles); // [Paul, George, Ringo]
Conversions between KtList
and KtMutableList
can be done with KtList.toMutableList()
and KtMutableList.toList()
;
final beatles = KtList.of("John", "Paul", "George", "Ringo");
final mutable = beatles.toMutableList();
mutable.removeAt(0);
print(mutable); // [Paul, George, Ringo]
print(beatles); // [John, Paul, George, Ringo]
kt_dart collections do not implement Iterable
. It is therefore not possible to directly iterate over the entries of a KtList
.
All kt_dart collections offer a .iter
property which exposes a Dart Iterable
. For-loops therefore don't look much different.
final beatles = KtList.of("John", "Paul", "George", "Ringo");
for (final member in beatles.iter) {
print(member);
}
Yes, alternatively you could use .asList()
instead which returns a Dart List
.
Kotlin users might be more familiar with the listOf()
and mutableListOf()
functions. Use them if you like but keep in mind that the dart community is much more used to use constructors instead of top-level functions.
final beatles = listOf("John", "Paul", "George", "Ringo");
final abba = mutableListOf("Agnetha", "Björn", "Benny", "Anni-Frid");
A KtSet
is a unordered collection of elements without duplicates.
Creating a KtSet
/KtMutableSet
is very similar to the KtList
API.
// Create a KtSet from scratch
final beatles = KtSet.of("John", "Paul", "George", "Ringo");
// Convert a existing Set to KtSet
final abba = {"Agnetha", "Björn", "Benny", "Anni-Frid"};
final immutableAbba = abba.toImmutableSet();
To create a KtMap
/KtMutableMap
start with Dart Map
and then convert it to a KtMap
with either:
pokemon.toImmutableMap(): KtMap
(since Dart 2.7)KtMap.from(pokemon): KtMap
pokemon.kt: KtMutableMap
(since Dart 2.7)KtMutableMap.from(pokemon): KtMutableMap
// immutable
final pokemon = {
1: "Bulbasaur",
2: "Ivysaur",
3: "Stegosaur",
}.toImmutableMap();
final newPokemon = KtMap.from({
152: "Chikorita",
153: "Bayleef",
154: "Meganium",
});
// mutable
final mutablePokemon = {
1: "Bulbasaur",
2: "Ivysaur",
3: "Stegosaur",
}.kt;
final newMutablePokemon = KtMutableMap.from({
152: "Chikorita",
153: "Bayleef",
154: "Meganium",
});
You may want to use a specific Map
implementation. kt_dart offers:
KtLinkedMap
- based on Darts LinkedHashMap
where the insertion order of keys is remembered and keys are iterated in the order they were inserted into the mapKtHashMap
- based on Darts HashMap
where keys of a HashMap
must have consistent [Object.==] and [Object.hashCode] implementations. Iterating the map's keys, values or entries (through [forEach]) may happen in any order.kt_dart offer two types of tuples, KtPair
with two elements and KtTriple
with three elements. They are used by some collection APIs and prevent a 3rd party dependency.
final beatles = KtList.of("John", "Paul", "George", "Ringo");
final partitions = beatles.partition((it) => it.contains("n"));
print(partitions.first); // [John, Ringo]
print(partitions.second); // [Paul, George]
There won't be a KtQuadruple
or TupleN
in this library. If you want to use tuples heavily in you application consider using the tuple
package. Better, use freezed
to generated data classes which makes for a much better API.
@nullable
Kotlin already has Non-Nullable types, something which is coming to Dart soon™. kt_dart already makes use of Non-Nullable Types and never returns null
unless a method is annotated with @nullable
.
There isn't any tooling which will warn you about the wrong usage but at least it's documented. And once nnbd lands in Dart it will be fairly easy to convert.
@nonNull
This annotation annotates methods which never return null
. Although this is the default in kt_dart, is makes it very obvious for methods which sometimes return null
in other languages.
@experimental
A method/class annotated with @experimental
marks the method/class as experimental feature. Experimental APIs can be changed or removed at any time.
Copyright 2019 Pascal Welsch
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Run this command:
With Dart:
$ dart pub add kt_dart
With Flutter:
$ flutter pub add kt_dart
This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get
):
dependencies:
kt_dart: ^1.1.0
Alternatively, your editor might support dart pub get
or flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:kt_dart/annotation.dart';
import 'package:kt_dart/collection.dart';
import 'package:kt_dart/exception.dart';
import 'package:kt_dart/kt.dart';
import 'package:kt_dart/standard.dart';
// ignore_for_file: avoid_print
import "package:kt_dart/kt.dart";
void main() {
/// Lists
final mapped = listOf(1, 2, 3, 4).map((it) => ">$it<");
print(mapped); // [>1<, >2<, >3<, >4<]
final flatMapped = listOf(1, 2, 3, 4).flatMap((it) => listOf(it * 2, it * 3));
print(flatMapped); // [2, 3, 4, 6, 6, 9, 8, 12]
final filtered = flatMapped.filter((it) => it % 3 == 0);
print(filtered); // [3, 6, 6, 9, 12]
final distinct = listFrom([1, 2, 3, 1, 2, 3]).distinct();
print(distinct); //[1, 2, 3]
/// Better equals
final kListEquals = listOf(12, 9, 6, 3) == listOf(12, 9, 6, 3);
print(kListEquals); // true
final dartListEquals = [12, 9, 6, 3] == [12, 9, 6, 3];
print(dartListEquals); // false
final kMapEquals = mapFrom({1: "Bulbasaur", 2: "Ivysaur"}) ==
mapFrom({1: "Bulbasaur", 2: "Ivysaur"});
print(kMapEquals); // true
final dartMapEquals =
{1: "Bulbasaur", 2: "Ivysaur"} == {1: "Bulbasaur", 2: "Ivysaur"};
print(dartMapEquals); // false
/// Sets
print(setOf(1, 2, 3, 1, 2, 3)); // [1, 2, 3]
/// Maps
final pokemon = mutableMapFrom({
1: "Bulbasaur",
2: "Ivysaur",
});
pokemon[1] = "Ditto";
print(pokemon); // {1=Ditto, 2=Ivysaur}
/// Tons of useful operators which *should* be part of the dart std lib
final numbers = listOf(1, 2, 3, 4);
print(numbers.sum()); // 10
final numbers5 = listOf(1, 2, 3, 4).sortedDescending();
print(numbers5); // [4, 3, 2, 1]
final beatles = setOf("John", "Paul", "George", "Ringo");
print(beatles); // [John, Paul, George, Ringo]
print(beatles.joinToString(
separator: "/",
transform: (it) => it.toUpperCase())); // JOHN/PAUL/GEORGE/RINGO
final grouped = beatles.groupBy((it) => it.length);
print(grouped); // {4=[John, Paul], 6=[George], 5=[Ringo]}
}
Download details:
Author: pascalwelsch.com
Source: https://github.com/passsy/kt.dart
#flutter #dart #kotlin #intellij-idea #ios #android #web #web-development