Dart

Dart

Dart is a client-optimized programming language for fast apps on multiple platforms. It is developed by Google and is used to build mobile, desktop, backend and web applications. Dart is an object-oriented, class defined, garbage-collected language using a C-style syntax that transcompiles optionally into JavaScript.
Zoie  Trantow

Zoie Trantow

1686227236

Flutter Pet Adoption app- 3 screens, Home page displays Pet list

Pet Adoption App

A Pet Adoption appllication using Flutter

Screenshots

.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'

Download details:

Author: bamakant
Source: https://github.com/bamakant/pet_adoption_app

#flutter #dart 

Flutter Pet Adoption app- 3 screens, Home page displays Pet list
Zoie  Trantow

Zoie Trantow

1686219780

Get country info based on user's IP in Flutter with this package

Ip_Country_Lookup

A Flutter package for retrieving country information based on a user's public IP address.

Features

Get the user's public IP address. Retrieve country information such as country code, country name, and ISP (Internet Service Provider).

In App Screenshots

Getting started

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.

Example


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",
                    ),
                  ),
                ],
              ),
      ),
    );
  }
}

Support

For any issues, questions, or feature requests, please email me at: afridi.khondakar@gmail.com.

Conclusion

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.


Download details:

Author: WorkWithAfridi
Source: https://github.com/WorkWithAfridi/IP_Country_Lookup-Package

License: MIT license

#flutter #dart 

Get country info based on user's IP in Flutter with this package
Zoie  Trantow

Zoie Trantow

1686212280

Flexible ticket and receipt view for Flutter: Simple and customizable

TicketViewFlutter

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

Installing

Add this to your package's pubspec.yaml file:

This library is posted in pub.dev

pubspec.yaml

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

PropertyTypeDescription
backgroundColorColorBackground card color for TicketView
contentBackgroundColorColorContent card color for TicketView

Screenshots

Default ViewReceipt View
alt textalt text

Author

  • Saurabh K Sharma - GIT

Download details:

Author: longhoang2984
Source: https://github.com/longhoang2984/ticket_view

License: BSD-3-Clause license

#flutter #dart 

Flexible ticket and receipt view for Flutter: Simple and customizable
Zoie  Trantow

Zoie Trantow

1686204804

Customizable Flutter grocery shop app UI for client-specific needs

Flutter Grocery App UI

Flutter Version Used : 1.22.4



Screens

  • Splash Screen
  • Welcome Screen
  • Home Screen(Shop)
  • Product Details Screen
  • Categories Screen (Explore)
  • Products Screen (After clicking any category)
  • Filter Screen
  • My Cart Screen
  • Checkout Bottom Sheet
  • Order Failed Dialog
  • Order Accepted Screen
  • Profile Screen

.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

Download details:

Author: sharvayasupport
Source: https://github.com/sharvayasupport/GroceryShopFlutter

#flutter #dart 

Customizable Flutter grocery shop app UI for client-specific needs
Zoie  Trantow

Zoie Trantow

1686199200

Weathergetx: A new Flutter project for weather forecasting

Weathergetx

A new Flutter project for weather forecasting.

Packages used

  1. flutter launcher icons
  2. Geocoding
  3. Geolocator
  4. Get
  5. http
  6. intl
  7. sleek circular slider

App UI

Screenshot 2023-06-04 145039 Screenshot 2023-06-04 145135 Screenshot 2023-06-04 145206 Screenshot 2023-06-04 145235

.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'

Download details:

Author: Harpreetsingh10414
Source: https://github.com/Harpreetsingh10414/WeatherX

#flutter #dart 

Weathergetx: A new Flutter project for weather forecasting
Zoie  Trantow

Zoie Trantow

1686197400

Transportation services app for individuals and businesses - Flutter

Inway

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.

Features

  • Light/dark mode toggle
  • Live previews
  • Fullscreen mode
  • Cross platform
  • Google Maps
  • Location decatet
  • Phone Auth
  • Beutfull Animations
  • Login with Google and Facebook

Installation

Clone the repository and run the following commands:

flutter pub get
flutter run

Tools

  • Bloc & Cubit
  • Firebase
  • Flutter
  • http
  • Google Maps
  • Animaitions

Demo

Video

Screenshots

IntroSingUpLogin
Intro_samsung-galaxys20ultra-cosmicblack-portraitPhone_samsung-galaxys20ultra-cosmicblack-portraitLogin_samsung-galaxys20ultra-cosmicblack-portrait
homemapactivity
home_samsung-galaxys20ultra-cosmicblack-portraitmap_samsung-galaxys20ultra-cosmicblack-portraitActiviity_samsung-galaxys20ultra-cosmicblack-portrait
ProfileSettingedit
Profile_samsung-galaxys20ultra-cosmicblack-portraitSettings_samsung-galaxys20ultra-cosmicblack-portraitEdit_samsung-galaxys20ultra-cosmicblack-portrait

.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: 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'

Download details:

Author: mohamed352
Source: https://github.com/mohamed352/Inway

#flutter #dart 

Transportation services app for individuals and businesses  - Flutter

Mutual Exclusion with Implementation Of Normal and Read-write Mutex

mutex 

A library for creating locks to ensure mutual exclusion when running critical sections of code.

Purpose

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.

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();
}

protect

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

Read-write mutex

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();
}

protectWrite and protectRead

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

When mutual exclusion is not needed

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.

Features and bugs

Please file feature requests and bugs at the issue tracker.

Use this package as a library

Depend on it

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.

Import it

Now in your Dart code, you can use:

import 'package:mutex/mutex.dart';

example/example.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

#flutter #android #web-development #web #dart #coding 

Mutual Exclusion with Implementation Of Normal and Read-write Mutex

Crop Any Widget/Image With Fancy and Customizable UI In Flutter

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).

Supported platforms

  • Flutter Android
  • Flutter iOS
  • Flutter Web
  • Flutter Desktop

Demo

Web Demo | Install from Google Play

Donation

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.

Screenshot

Screenshot

Getting Started

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.

Use this package as a library

Depend on it

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.

Import it

Now in your Dart code, you can use:

import 'package:crop/crop.dart';

example/lib/main.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 

Crop Any Widget/Image With Fancy and Customizable UI In Flutter

A Flutter And Dart Package For Using Jalali Calendar

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.

Key Features

  • Convert between Jalali, Gregorian and Flutter's DateTime objects.
  • Access year, month, day, weekday, Julian day number, month length and ... through getters.
  • Format Jalali and Georgian dates with an easy and powerful syntax using DateFormatter.
  • Ensure Jalali and Georgian dates validity.
  • Check if a Jalali or Gregorian year is leap.
  • Immutable date objects with copy methods for easy manipulation.
  • Compare Dates easily with comparison operators or by using Comparable.
  • Add or subtract days with + and - operators.
  • Find distance between dates by methods and ^ operator.
  • Add years, months and days separately or as a combination with methods.
  • High code coverage with a lot of unit tests.
  • Null-Safe API
  • Support time information

Recent Changes

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.

Issues and feature requests

If you want a new feature, or you found an issue, please make an issue on GitHub, so I can see your request.

Usage

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:

  • y: year (whatever length it has). year should be positive.
  • yy: two digit year. year should be between 1000 and 9999.
  • yyyy: four digit year. year should be between 0 and 9999.
  • m: month (whatever length it has).
  • mm: two-digit month.
  • mN: month name.
  • d: day (whatever length it has).
  • dd: two digit day.
  • wN: week day name.

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

Example

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);
}

Use this package as a library

Depend on it

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.

Import it

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

#flutter #android #web-development #web #dart 

A Flutter And Dart Package For Using Jalali Calendar
Bulah  Pfeffer

Bulah Pfeffer

1686122930

Building a Custom Widget in Flutter

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.

What is a Custom Widget?

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.

Why Use Custom Widgets?

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.

Code Reusability

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.

Maintainability

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.

Consistent UI

They also enable developers to define a consistent and unified UI design across their application.

Abstraction

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.

How to Build a Custom Widget in Flutter

Let’s start building our custom widget.

Clone the Repo

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

image-67

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

image-68

Install Flutter dependencies

Run the App

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.

image-69

Initial app run screen

If you’ve come this far, that’s great.

Analyze the Code

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:

image-72

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.

Refactor the Code and Build a Custom Widget

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.

Integrate the Custom Widget

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:

image-73

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/

image-74

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.

Why Build a 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

image-75

Changes in our custom widget

But if you hadn't refactored your code, you'd have to make this change in 20 places.

image-76

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.

Use Cases for Custom Widgets

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.

Conclusion

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 

Building a Custom Widget in Flutter
Flutter App

Flutter App

1686106385

Flutter 3.3 Master Class| Build a Ticket Booking App using Flutter

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 

#flutter #dart  

Flutter 3.3 Master Class| Build a Ticket Booking App using Flutter

Pre-built Widgets /Utilites to Integrate Firebase Firestore In Flutter

Firebase UI for Firestore 

Firebase UI for Firestore enables you to easily integrate your application UI with your Cloud Firestore database.

Installation

flutter pub add cloud_firestore
flutter pub add firebase_ui_firestore

Usage

Import the Firebase UI for Firestore package:

import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';

Infinite scrolling

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,
  // ...
);

Controlling page size

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.

Using typed responses

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);
  },
);

Loading and error handling

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),
  // ...
);

Advanced configuration

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:

  1. Within the itemBuilder of our GridView, we have to manually ensure that we call the fetchMore() method on the snapshot when more data is required.
  2. The FirestoreQueryBuilder does not provide a list-view based handler, instead you must provide your own implementation.

Use this package as a library

Depend on it

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.

Import it

Now in your Dart code, you can use:

import 'package:firebase_ui_firestore/firebase_ui_firestore.dart';

example/lib/main.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 

Pre-built Widgets /Utilites to Integrate Firebase Firestore In Flutter

A Cookie Manager for Http Requests In Dart

A cookie manager for http requests in Dart, help you to deal with the cookie policies and persistence.

Get started

Checkout the Migration Guide for breaking changes between versions.

Add dependency

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

Usage

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);
}

Classes

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),
);

Storage

You can customize your own storage by extending Storage, see FileStorage for more details.

APIs

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.

Working with 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);

Working with dio

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.

Copyright & License

The project and it's underlying projects are originally authored by @wendux with the organization @flutterchina since 2018.

The project consents the MIT license.

Use this package as a library

Depend on it

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.

Import it

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

#flutter #android #web-development #web #dart #http 

A Cookie Manager for Http Requests In Dart

A Port Of Functional LINQ From The .NET Library For Dart

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.

API Reference

Usage

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.)

Tuples

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:

  • Constant constructors allow for efficient use of known tuple values.
  • Includes access to the item(s) by getter (tuple.item2) or by indexer(tuple[2]). (Note that access by indexer is not type-safe)
  • Factory constructor fromJson and method toJson means tuples are seralization-ready.
  • Additional factory constructor fromList to generate a tuple from a list (automatically casting when specifying type parameters for the constructor).
  • An 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).
    • Additionally, there is an asDynamic convenience method for casting a tuple to dynamic items.
  • Although tuples themselves are immutable, a copyWith method allows easy generation of duplicate tuples, optionally specifying new values for specific items.
    • Additionally, a copyWithout method allows selective filtering of items from a tuple resulting in a lower-order tuple.
  • A mapActions method allows you to iterate over each item with an exhaustive list of type-safe callbacks.
  • Each tuple class extends Iterable<dynamic>, so it can be treated as a normal iterable (and thus combined with any darq extension method).
  • As == and hashCode are both implemented, tuples can be directly compared for equality or used as keys for maps and other hash sets.
  • (2.0.0) Can be converted to and from Dart 3 records.

MoreLINQ Extension Methods

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'] ]

New Iterable Types

String Extension Methods

Map Extension Methods

Iterable Extension Methods

Use this package as a library

Depend on it

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.

Import it

Now in your Dart code, you can use:

import 'package:darq/darq.dart';

example/main.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

#flutter #dart #linq #dotnet #function 

A Port Of Functional LINQ From The .NET Library For Dart

A Port Of Kotlin-stdlib for Dart/Flutter Projects

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';

Motivation

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

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

// 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();

Create a KtMutableList

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]

Mutable/Immutable conversion

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]

for loop

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 syntax

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");

KtSet

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();

KtMap

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",
});

KtHashMap and KtLinkedMap

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 map
  • KtHashMap - 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.

KtPair, KtTriple

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.

Annotations

@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.

https://github.com/passsy/kt.dart/blob/490a3b205ffef27d9865d6018381a4168119e69f/lib/src/collection/kt_map.dart#L51-L53

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.

License

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.

Use this package as a library

Depend on it

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.

Import it

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';

example/main.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 

A Port Of Kotlin-stdlib for Dart/Flutter Projects