Callbag-partition: Callbag Operator That Splits The Source into Two

callbag-partition

Callbag operator that splits the source into two, one with values that satisfy a predicate, and another with values that don't satisfy the predicate.

Example

import forEach from 'callbag-for-each'
import fromIter from 'callbag-from-iter'
import partition from 'callbag-partition'
import pipe from 'callbag-pipe'

const [even$, odd$] = pipe(
  fromIter([1, 2, 3, 4, 5, 6]),
  partition(i => i % 2 === 0),
)

pipe(
  even$,
  forEach(val => {
    // will log 2, 4, 6
    console.log(val)
  }),
)

pipe(
  odd$,
  forEach(val => {
    // will log 1, 3, 5
    console.log(val)
  }),
)

Author: Andarist
Source Code: https://github.com/Andarist/callbag-partition 
License: 

#javascript #operator 

Callbag-partition: Callbag Operator That Splits The Source into Two

Callbag Operator That Drops Consecutive Duplicate

callbag-drop-repeats

Drops consecutive duplicate values. Works on either pullable or listenable sources. Takes an optional custom predicate function. If not provided then (a, b) => a === b will be used.

npm install callbag-drop-repeats

Examples

Listenables

const dropRepeats = require('callbag-drop-repeats');
const { forEach, map, interval, pipe, take } = require('callbag-basics');

pipe(
  interval(1000),
  take(3),
  map(() => 'Always me, but once'),
  dropRepeats(),
  forEach((x) => {
    console.log(x); // 'Always me, but once'
  })
);

Pullables

Without a predicate function:

const dropRepeats = require('callbag-drop-repeats');
const { forEach, fromIter, pipe } = require('callbag-basics');

pipe(
  fromIter([0, 0, 0, 1]),
  dropRepeats(),
  forEach((x) => {
    console.log(x); // 0
  })                // 1
);

With a predicate function:

const dropRepeats = require('callbag-drop-repeats');
const { forEach, fromIter, pipe } = require('callbag-basics');

pipe(
  fromIter([{ name: 'A' }, { name: 'A' }, { name: 'B' }]),
  dropRepeats((prev, curr) => prev.name === curr.name),
  forEach((x) => {
    console.log(x); // { name: 'A' }
  })                // { name: 'B' } 
);

Author: franciscotln
Source Code: https://github.com/franciscotln/callbag-drop-repeats 
License: MIT license

#javascript #drop #operator 

Callbag Operator That Drops Consecutive Duplicate

Callbag-debounce: Debounce Operator for Callbag

Callbag debounce

Debounce operator for Callbag

Install

npm i callbag-debounce

Example

Debounces the scroll event and runs the expensiveFunction only when there is a 60ms pause.

const { fromEvent, forEach, pipe } = require("callbag-basics");
import { debounce } from "callbag-debounce";

pipe(
  fromEvent(document, "scroll"),
  debounce(60),
  forEach(expensiveFunction)
);

Changelog

4.0.0 (21/06/2021)

BREAKING CHANGE:

  • Last value is flushed even if the stream is receiving a terminate signal before the value has been debounced (fixes #12)

Before

pipe(
  of(42),
  debounce(1000),
  subscribe(console.log)
)
// Terminates without logging anything

After

pipe(
  of(42),
  debounce(1000),
  subscribe(console.log)
)
// logs 42
// Then terminates

3.0.0 (05/03/2021)

BREAKING CHANGE:

  • Timer is cleared when stream is terminated by sink (see #13)

v2.1.0 (05/03/2018)

  • error (t === 2 && d !== undefined) signals are sent right away (previously they were delayed according to the wait parameter);
  • complete (t === 2 && d === undefined) signals are sent when the last value is debounced (previously they were debounced according to the wait parameter).

v2.0.0 (01/03/2018)

codebase migrated to TypeScript. The module needs to be imported via a named import

import { debounce } from "callbag-debounce";

previously

import debounce from "callbag-debounce";

Author: Atomrc
Source Code: https://github.com/atomrc/callbag-debounce 
License: MIT license

#typescript #operator 

Callbag-debounce: Debounce Operator for Callbag

Callbag-throttle: Throttle Operator for Callbags

callbag-throttle

Like RxJS' throttleTime, this callbag operator transforms a pullable or listenable source into a listenable that enforces a fixed-time cooldown period. Any data that arrives during the quiet time is ignored.

(See callbag-lossless-throttle for a lossless version that queues up arrivals during the cooldown quiet period. This repo originally implemented lossless throttling but was changed to match RxJS users' intuition.)

Background

Installation

On the command line in your Node.js app, run

$ npm install --save callbag-throttle

Then load it via

const throttle = require('callbag-throttle');

API

With const throttle = require('callbag-throttle'), insert the following operator between a source and a sink to make the source a throttled listenable, i.e., a source that enforces a delay (in milliseconds) between data:

throttle(delayMilliseconds)

The termination signal from the original source is not throttled if the original source was listenable, i.e., a sink will be notified immediately of a listenable's termination. However, this is not the case for a pullable: a sink will be notified of a throttled pullable's termination only after the delay (since the pullable is asked for more data only after the cooldown quiet time expires; it's possible that a better design can obviate this caveat?).

Examples

All examples use the following setup code, and use Node's process.hrtime:

// SETUP CODE
const throttle = require('callbag-lossless-throttle');
const { fromIter, interval, take, pipe } = require('callbag-basics');
// Let's define a slightly different forEach that shows us when the source terminates:
const forEachWithFinal = operation => source => {
  let talkback;
  source(0, (t, d) => {
    if (t === 0) talkback = d;
    if (t === 1) operation(d);
    if (t === 1 || t === 0) talkback(1);
    if (t === 2) { operation("TERMINATED"); }
  });
};
function elapsed(start) {
  const end = process.hrtime(start);
  return Math.round(end[0] * 1000 + end[1] / 1e6);
}

If we gently throttle an already-slow listenable, the resulting callbag is indistinguishable from the unthrottled case:

var tic = process.hrtime();
pipe(interval(100), take(5), forEachWithFinal(x => console.log(`${x}: ${elapsed(tic)} ms: original`)));
pipe(interval(100), take(5), throttle(90),
    forEachWithFinal(x => console.log(`${x}: ${elapsed(tic)} ms: (not really) throttled`)));
// 0: 107 ms: original
// 0: 110 ms: (not really) throttled
// 1: 211 ms: original
// 1: 211 ms: (not really) throttled
// 2: 313 ms: original
// 2: 313 ms: (not really) throttled
// 3: 413 ms: original
// 3: 413 ms: (not really) throttled
// 4: 514 ms: original
// TERMINATED: 514 ms: original
// 4: 515 ms: (not really) throttled
// TERMINATED: 515 ms: (not really) throttled

This next example shows real throttling: only three of five data points from the source are seen after throttling. This is a listenable example again:

var tic = process.hrtime();
pipe(interval(100), take(5), forEach(x => console.log(`${x}: ${elapsed(tic)} ms: original`)));
pipe(interval(100), take(5), throttle(150), forEach(x => console.log(`${x}: ${elapsed(tic)} ms: throttled to 150 ms`)));
// 0: 102 ms: original
// 0: 105 ms: throttled to 150 ms
// 1: 206 ms: original
// 2: 306 ms: original
// 2: 306 ms: throttled to 150 ms
// 3: 408 ms: original
// 4: 513 ms: original
// TERMINATED: 513 ms: original
// 4: 513 ms: throttled to 150 ms
// TERMINATED: 514 ms: throttled to 150 ms

Notice how in both these examples, the original source's termination is passed on without delay.

This is in contrast to a pullable source. After throttling, it becomes a listenable source, but the delay has to be applied between the last non-terminate data and the termination:

var tic = process.hrtime();
pipe(fromIter([ 10, 20, 30, 40 ]), forEach(x => console.log(`${x}: ${elapsed(tic)} ms: original`)));
pipe(fromIter([ 10, 20, 30, 40 ]), throttle(50), forEach(x => console.log(`${x}: ${elapsed(tic)} ms: throttled`)));
// 10: 1 ms: original
// 20: 2 ms: original
// 30: 3 ms: original
// 40: 3 ms: original
// TERMINATED: 3 ms: original
// 10: 3 ms: throttled
// 20: 55 ms: throttled
// 30: 107 ms: throttled
// 40: 159 ms: throttled
// TERMINATED: 211 ms: throttled

Author: Fasiha
Source Code: https://github.com/fasiha/callbag-throttle 
License: Unlicense license

#javascript #operator 

Callbag-throttle: Throttle Operator for Callbags

Callbag-rescue: Callbag Operator That Rescues A Failed Source

callbag-rescue

Callbag higher-order operator that rescues a failed source. Works on either pullable or listenable sources.

npm install callbag-rescue

examples

listenables

After a failed promise, rescue with a new Callbag source:

 const forEach = require('callbag-for-each');
 const fromPromise = require('callbag-from-promise');
 const of = require('callbag-of');
 const map = require('callbag-map');
 const pipe = require('callbag-pipe');
 const rescue = require('callbag-rescue'); 

pipe(
  fromPromise(Promise.reject({ status: 404 })),
  map(res => res.body),
  rescue(err => of({ type: 'ERROR', payload: err })),
  forEach(n => {
    console.log(n); // { type: 'ERROR', payload: { status: 404 } }
  })
);

pullables

After a source Callbag throws an error, rescue it:

const forEach = require('callbag-for-each');
const fromIter = require('callbag-from-iter');
const map = require('callbag-map');
const of = require('callbag-of');
const pipe = require('callbag-pipe');

pipe(
  fromIter([1, 2]),
  map(num => num.a.name),
  rescue(err => of({ type: 'ERROR', payload: err.message })),
  forEach(n => {
    console.log(n); // { type: 'ERROR', payload: 'Cannot read property "name" of undefined' }
  })
);

Author: Franciscotln
Source Code: https://github.com/franciscotln/callbag-rescue 
License: MIT license

#javascript #operator 

Callbag-rescue: Callbag Operator That Rescues A Failed Source

Callbag-lossless-throttle: Lossless Throttle Operator for Callbags

callbag-lossless-throttle

Callbag operator that transforms either a pullable or listenable source into a listenable that enforces a fixed time delay between emissions. If the source is listenable and produces data faster than the delay, data is queued up internally and emitted in the order received, making this a lossless throttle operator. No data is dropped. (Compare to callbag-throttle for a lossy version.)

Example usecase: a callbag source is generating URLs on a server with rate limits of, say, one request per second. This operator can be used to throttle the source to 1000 milliseconds:

const throttle = require('callbag-lossless-throttle');
const forEach = require('callbag-for-each');

pipe(urlSource, throttle(1000), forEach(url => fetch(url)));

Background

I feel I have to give some background on callbags, which is the name André Staltz gave to the framework for streams and iterables he invented (some might say "discovered" here). If you're like me and learn by seeing examples, the callbag-basics module is probably the best place to start, then following the links therein, and finally by scanning the results for searching GitHub for "callbag", since Staltz has made callbag-basics very basic and wants to see functionality added through stand-alone modules that meet the basic callbag specification.

Installation

On the command line in your Node.js app, run

$ npm install --save callbag-lossless-throttle

Then load it via

const throttle = require('callbag-lossless-throttle');

API

With const throttle = require('callbag-lossless-throttle'), insert the following operator between a source and a sink to make the source a throttled listenable, i.e., a source that enforces a delay (in milliseconds) between data. Even the termination signal from the original source is throttled (is that ok?).

throttle(delayMilliseconds)

Example

Let's use Node's process.hrtime to show how this works in a few difference cases.

Throttling a listenable callbag that emits slow enough doesn't make a difference.

const throttle = require('callbag-lossless-throttle');
const { fromIter, interval, take, forEach, pipe } = require('callbag-basics');
function elapsed(start) {
  const end = process.hrtime(start);
  return Math.round(end[0] * 1000 + end[1] / 1e6);
}
var tic = process.hrtime();
pipe(interval(100), take(5), forEach(x => console.log(`${elapsed(tic)} ms: original`)));
pipe(interval(100), take(5), throttle(50), forEach(x => console.log(`${elapsed(tic)} ms: (not really) throttled`)));
// 102 ms: original
// 105 ms: (not really) throttled
// 203 ms: original
// 205 ms: (not really) throttled
// 305 ms: original
// 305 ms: (not really) throttled
// 405 ms: original
// 405 ms: (not really) throttled
// 505 ms: original
// 506 ms: (not really) throttled

As you can see, throtlling a listenable emitting every hundred milliseconds by fifty milliseconds doesn't change things: both emit almost in lockstep. But if we throttle it at, say, 200 milliseconds, the difference becomes obvious: both start immediately but:

var tic = process.hrtime();
pipe(interval(100), take(5), forEach(x => console.log(`${elapsed(tic)} ms: original`)));
pipe(interval(100), take(5), throttle(200), forEach(x => console.log(`${elapsed(tic)} ms: throttled`)));
// 103 ms: original
// 106 ms: throttled
// 211 ms: original
// 312 ms: throttled
// 312 ms: original
// 414 ms: original
// 513 ms: throttled
// 514 ms: original
// 715 ms: throttled
// 920 ms: throttled

Note how the throttled callbag is lossless: data is queued internally.

With pullable callbags, note how both callbags begin immediately, but the unthrottled forEach zips through the source in four milliseconds. Meanwhile, the throttled callbag continues its sedate pace:

var tic = process.hrtime();
pipe(fromIter([ 10, 20, 30, 40 ]), forEach(x => console.log(`${elapsed(tic)} ms: original`)));
pipe(fromIter([ 10, 20, 30, 40 ]), throttle(50), forEach(x => console.log(`${elapsed(tic)} ms: throttled`)));
// 0 ms: original
// 2 ms: original
// 3 ms: original
// 3 ms: original
// 4 ms: throttled
// 56 ms: throttled
// 112 ms: throttled
// 177 ms: throttled

Tutorial

This is my first callbag library, and my second day working with callbags. I am very much open to suggestions for improving this library.

In this section, I'd like to present some notes on how I got to this library, with the aim of showing callbag beginners how I started thinking about it. I knew I needed a throttle mechanism because I had an ES2015 generator (cartesian-product-generator) that was eventually going to hit a REST endpoint enforcing rate limits, so I pasted the source code for a source, callbag-from-iter, an operator, callbag-map, and a sink, callbag-for-each, and stared at those for a while.

Then I gathered my courage to start tweaking forEach, which André gives as

const forEach = operation => source => {
  let talkback;
  source(0, (t, d) => {
    if (t === 0) talkback = d;
    if (t === 1) operation(d);
    if (t === 1 || t === 0) talkback(1);
  });
};

in order to add throttling. To keep it simple, I aimed to get a throttled forEach that only worked for listenables. I got the following:

const forEachThrottledListenables = (operation, milliseconds) => source => {
  let talkback;
  let stuffListenablesHaveGivenMe = [];
  let timeout;
  const runAfterCooldown = () => {
    timeout = null;
    if (stuffListenablesHaveGivenMe.length) {
      operation(stuffListenablesHaveGivenMe.shift());
      timeout = setTimeout(runAfterCooldown, milliseconds);
    }
  };
  source(0, (t, d) => {
    if (t === 0) talkback = d;
    if (t === 1) {
      if (timeout) {
        stuffListenablesHaveGivenMe.push(d);
      } else {
        operation(d);
        timeout = setTimeout(runAfterCooldown, milliseconds);
      }
    }
  });
};

The conceptual differences between forEachThrottledListenables and forEach is that when the sink receives data (t===1), it'll check if it's in a cooldown quiet period by checking for a timeout in progress. If there's no timeout counting down, it'll do the same thing as the original forEach and then kick off the timeout to enforce the throttle delay. If there is a timeout in progress, it'll push the data that the listenable source gave it to a FIFO stack (an array). The timeout triggers the runAfterCooldown function which clears the timeout variable (which would otherwise be a big Node object, even after completion), checks if any data had gotten queued up while it was counting down, and if so, emits that and kicks off the next time out. An important idea is, every time operation is called (i.e., the actual side-effect of the sink, e.g., console.log), a timeout has to be set.

You can try the above function:

var { pipe, interval, take } = require('callbag-basics');
var bag = pipe(interval(100), take(5));
forEachThrottledListenables(x => { console.log('(not) throttled bag', x); }, 50)(bag);
forEachThrottledListenables(x => { console.log('REALLY throttled bag', x); }, 300)(bag);

Two listenable source callbags with an interval of 100 milliseconds are firing. The one is "throttled" to fifty milliseconds, which should be indistinguishable from the original source. The second is throttled three-fold (300 milliseconds), so while both sinks log at the same time, more of the first's messages are printed before the second's.

My next goal was the extend this to a throttled forEach that works for both listenable and pullables. It's freakish that the tweaks I had to add to get pullables to work are almost completely unrelated to anything involving listenables.

Here's the full code for forEachThrottledBoth, and then I'll show the diff between the two:

const forEachThrottledBoth = (operation, milliseconds) => source => {
  let talkback;
  let stuffListenablesHaveGivenMe = [];
  let timeout;
  let terminated = false;
  const runAfterCooldown = () => {
    timeout = null;
    if (!terminated) { talkback(1); }
    if (stuffListenablesHaveGivenMe.length) {
      operation(stuffListenablesHaveGivenMe.shift());
      timeout = setTimeout(runAfterCooldown, milliseconds);
    }
  };
  source(0, (t, d) => {
    if (t === 0) {
      talkback = d;
      talkback(1);
    }
    if (t === 1) {
      if (timeout) {
        stuffListenablesHaveGivenMe.push(d);
      } else {
        operation(d);
        timeout = setTimeout(runAfterCooldown, milliseconds);
      }
    }
    if (t === 2) { terminated = true; }
  });
};

And now the diffs (omitting the change in function name):

4a5
>   let terminated = false;
6a8
>     if (!terminated) { talkback(1); }
13c15,18
<     if (t === 0) talkback = d;
---
>     if (t === 0) {
>       talkback = d;
>       talkback(1);
>     }
21a27
>     if (t === 2) { terminated = true; }

This diff should show you what I mean by freaky separation of concerns (which might have been coincidental). I didn't touch any of the existing code—I just added code to work with pullables, meaning, I now have to track it's termination, and I have to invoke its talkback after handshake and after every data message I get from it.

I think that's really cool! By this point, late at night, sick with cold and cough and chills, I'm hooked on callbags.

So I could have used just this throttled sink to accomplish my goal but I was also sufficiently enamored to try and make an operator, like callbag-map, to work with any sink. This needed a good bit more understanding:

  • the one extra level of functional nesting, i.e., compare the above sink, forEachThrottledBoth = (operation, milliseconds) => source => {} to throttle = milliseconds => source => (start, sink) => {},
  • that I have to handshake the sink, not just the source, and track its termination too,
  • convey both data and termination from the source to the sink, not just the data,
  • and just in general get a better sense of what callbags are doing, before I arrived at the present library.

Maybe this can be helpful to someone who's starting out wondering how to make callbags do something new.

(Note, this library was originally called callbag-throttle but André alerted me to RxJS' lossy throttle operator, so this was renamed to avoid confusion.)

Author: Fasiha
Source Code: https://github.com/fasiha/callbag-lossless-throttle 
License: Unlicense license

#javascript #operator

Callbag-lossless-throttle: Lossless Throttle Operator for Callbags

Callbag-flatten-iter: Callbag Operator That Flattens Iterables

Callbag-flatten-iter

Callbag Operator That Flattens Iterables

yarn add callbag-flatten-iter
const pipe = require("callbag-pipe");
const fromIter = require("callbag-from-iter");
const flattenIter = require("callbag-flatten-iter");
const forEach = require("callbag-for-each");

pipe(
  fromIter([[10], [20], [30], [40, 50, 60]]),
  flattenIter,
  forEach(i => {
    // called 6 times
  })
);

If you're blessed with the pipeline operator:

fromIter([[10], [20], [30], [40, 50, 60]])
  |> flattenIter
  |> forEach(i => {
    // called 6 times
  });

Learn more

Author: AAronshaf
Source Code: https://github.com/aaronshaf/callbag-flatten-iter 
License: MIT license

#javascript #operator 

Callbag-flatten-iter: Callbag Operator That Flattens Iterables

Callbag Operator That Flattens A Higher-order Callbag Source

callbag-flatten

Callbag operator that flattens a higher-order callbag source. Like RxJS "switch" or xstream "flatten". Use it with map to get behavior equivalent to "switchMap". Works on either pullable or listenable sources.

npm install callbag-flatten

examples

listenables

On each mouse click, start a stopwatch ticking every second:

const fromEvent = require('callbag-from-event');
const interval = require('callbag-interval');
const flatten = require('callbag-flatten');
const observe = require('callbag-observe');
const pipe = require('callbag-pipe');
const map = require('callbag-map');

const source = pipe(
  fromEvent(document, 'click'),
  map(() => interval(1000)),
  flatten,
  observe(x => console.log(x))
);

pullables

Loop over two iterables (such as arrays) and combine their values together:

const fromIter = require('callbag-from-iter');
const iterate = require('callbag-iterate');
const flatten = require('callbag-flatten');
const pipe = require('callbag-pipe');
const map = require('callbag-map');

const source = pipe(
  fromIter('hi'),
  map(char => pipe(
    fromIter([10, 20, 30]),
    map(num => char + num)
  )),
  flatten,
  iterate(x => console.log(x))
);

// h10
// h20
// h30
// i10
// i20
// i30

Author: Staltz 
Source Code: https://github.com/staltz/callbag-flatten 
License: MIT license

#javascript #operator #source 

Callbag Operator That Flattens A Higher-order Callbag Source
伊藤  直子

伊藤 直子

1650021960

TODO APPを構築して、JavaScriptでのCRUD操作を学ぶ

今日は、Todoアプリを作成してJavaScriptでCRUD操作を行う方法を学びます。始めましょう🔥

これは私たちが今日作っているアプリです:

今日作っているアプリ

CRUDとは何ですか?

画像の説明

CRUDは-の略です

  • C:作成
  • R:読む
  • U:更新
  • D:削除

CRUDフルフォーム

CRUDは、データの作成、データの読み取り、編集、およびそれらのデータの削除を可能にするメカニズムの一種です。この例では、Todoアプリを作成するので、タスクの作成、タスクの読み取り、タスクの更新、またはタスクの削除を行うための4つのオプションがあります。

CRUDの原則を理解する

チュートリアルを開始する前に、まず、CRUDの原則を理解しましょう。そのために、非常に単純なソーシャルメディアアプリケーションを作成しましょう。

ソーシャルメディアアプリプロジェクト

設定

プロジェクトの設定

このプロジェクトでは、以下の手順に従います。

  • index.html、style.css、main.jsという名前の3つのファイルを作成します
  • JavaScriptおよびCSSファイルをindex.htmlにリンクします
  • ライブサーバーを起動します

HTML

bodyタグ内に、クラス名を使用してdivを作成します.container。そこには2つのセクションがあり.left.right👇

<body>
  <h1>Social Media App</h1>
  <div class="container">

    <div class="left"></div>
    <div class="right"></div>

  </div>
</body>

左側に投稿を作成します。右側では、投稿を表示、更新、削除できます。次に、.leftdivタグ内にフォームを作成します👇

<div class="left">
  <form id="form">
    <label for="post"> Write your post here</label>
    <br><br>
    <textarea name="post" id="input" cols="30" rows="10"></textarea>
    <br> <br>
    <div id="msg"></div>
    <button type="submit">Post</button>
  </form>
</div>

このコードをHTML内に記述して、投稿を右側に表示できるようにします👇

<div class="right">
  <h3>Your posts here</h3>
  <div id="posts"></div>
</div>

次に、プロジェクトでそのフォントを使用するために、headタグ内にfont-awesomeCDNを挿入します👇

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" />

次に、削除アイコンと編集アイコンを使用してサンプル投稿を作成します。このコードをdiv内にID#postsで記述します:👇

<div id="posts">
  <div>
    <p>Hello world post 1</p>
    <span class="options">
      <i class="fas fa-edit"></i>
      <i class="fas fa-trash-alt"></i>
    </span>
  </div>

  <div >
    <p>Hello world post 2</p>
    <span class="options">
      <i class="fas fa-edit"></i>
      <i class="fas fa-trash-alt"></i>
    </span>
  </div>
</div>

これまでの結果は次のようになります。

HTMLマークアップの結果

CSS

プロジェクト1のCSSの追加

シンプルにしましょう。スタイルシート内にこれらのスタイルを記述します:👇

body {
  font-family: sans-serif;
  margin: 0 50px;
}

.container {
  display: flex;
  gap: 50px;
}

#posts {
  width: 400px;
}

i {
  cursor: pointer;
}

次に、投稿divとオプションアイコンに次のスタイルを記述します。👇

#posts div {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.options {
  display: flex;
  gap: 25px;
}

#msg {
  color: red;
}

これまでの結果は次のようになります:👇

cssパーツプロジェクト1を追加した後の結果

JavaScriptパート

javascript部分を開始します

このフローチャートに従って、プロジェクトを進めていきます。心配しないでください、私は途中ですべてを説明します。👇

フローチャート

フォームの検証

まず、JavaScriptのHTMLからすべてのIDセレクターをターゲットにしましょう。このように:👇

let form = document.getElementById("form");
let input = document.getElementById("input");
let msg = document.getElementById("msg");
let posts = document.getElementById("posts");

次に、フォームの送信イベントリスナーを作成して、アプリのデフォルトの動作を防ぐことができるようにします。同時に、という名前の関数を作成しますformValidation。👇

form.addEventListener("submit", (e) => {
  e.preventDefault();
  console.log("button clicked");

  formValidation();
});

let formValidation = () => {};

次に、関数内でifelseステートメントを作成しformValidationます。これにより、ユーザーが空白の入力フィールドを送信するのを防ぐことができます。👇

let formValidation = () => {
  if (input.value === "") {
    msg.innerHTML = "Post cannot be blank";
    console.log("failure");
  } else {
    console.log("successs");
    msg.innerHTML = "";
  }
};

これまでの結果は次のとおりです:👇

7sb8faq21j5dzy9vlswj

ご覧のとおり、ユーザーがフォームを空白で送信しようとすると、メッセージも表示されます。

入力フィールドからデータを受け入れる方法

入力フィールドから取得するデータが何であれ、それらをオブジェクトに格納します。。という名前のオブジェクトを作成しましょうdata。そして、次の名前の関数を作成しますacceptData:👇

let data = {};

let acceptData = () => {};

主なアイデアは、関数を使用して、入力からデータを収集し、という名前のオブジェクトに格納することdataです。acceptDataそれでは、関数の作成を完了しましょう。

let acceptData = () => {
  data["text"] = input.value;
  console.log(data);
};

また、acceptDataユーザーが送信ボタンをクリックしたときに機能する機能が必要です。そのために、関数のelseステートメントでこの関数を起動しformValidationます。👇

let formValidation = () => {
  if (input.value === "") {
    // Other codes are here
  } else {
    // Other codes are here
    acceptData();
  }
};

データを入力してフォームを送信すると、コンソールにユーザーの入力値を保持しているオブジェクトが表示されます。このように:👇

これまでのコンソールでの結果

JavaScriptテンプレートリテラルを使用して投稿を作成する方法

入力データを右側に投稿するには、div要素を作成し、それを投稿divに追加する必要があります。まず、関数を作成して次の行を記述しましょう:👇

let createPost = () => {
  posts.innerHTML += ``;
};

バックティックはテンプレートリテラルです。それは私たちのテンプレートとして機能します。ここでは、親div、入力自体、および編集アイコンと削除アイコンを保持するオプションdivの3つが必要です。機能を終了しましょう👇

let createPost = () => {
  posts.innerHTML += `
  <div>
    <p>${data.text}</p>
    <span class="options">
      <i onClick="editPost(this)" class="fas fa-edit"></i>
      <i onClick="deletePost(this)" class="fas fa-trash-alt"></i>
    </span>
  </div>
  `;
  input.value = "";
};

acceptdata関数では、関数を起動しますcreatePost。このように:👇

let acceptData = () => {
  // Other codes are here

  createPost();
};

これまでの結果:👇

これまでの結果

これまでのところ、プロジェクト1はほぼ完了しています。

ここまでは順調ですね

投稿を削除する方法

投稿を削除するために、まず、javascriptファイル内に関数を作成しましょう。

let deletePost = (e) => {};

deletePost次に、onClick属性を使用して、すべての削除アイコン内でこの関数を起動します。これらの行は、HTMLとテンプレートリテラルで記述します。👇

<i onClick="deletePost(this)" class="fas fa-trash-alt"></i>

thisキーワードは、イベントを発生させた要素を参照します。この場合、thisは削除ボタンを指します。

注意深く見てください。削除ボタンの親は、クラス名オプションのあるスパンです。スパンの親はdivです。parentElementしたがって、削除アイコンからdivにジャンプし、直接ターゲットにして削除できるように、2回書き込みます。

関数を終了しましょう。👇

let deletePost = (e) => {
  e.parentElement.parentElement.remove();
};

これまでの結果:👇

投稿結果の削除

投稿を編集する方法

投稿を編集するために、まず、JavaScriptファイル内に関数を作成しましょう。

let editPost = (e) => {};

editPost次に、onClick属性を使用して、すべての編集アイコン内でこの関数を起動します。これらの行は、HTMLとテンプレートリテラルで記述します。👇

<i onClick="editPost(this)" class="fas fa-edit"></i>

thisキーワードは、イベントを発生させた要素を参照します。この場合、thisは編集ボタンを指します。

注意深く見てください。編集ボタンの親は、クラス名オプションのあるスパンです。スパンの親はdivです。parentElementしたがって、編集アイコンからdivにジャンプし、直接ターゲットにして削除できるように、2回書き込みます。

次に、投稿内のデータが何であれ、入力フィールドに戻して編集します。

関数を終了しましょう。👇

let editPost = (e) => {
  input.value = e.parentElement.previousElementSibling.innerHTML;
  e.parentElement.parentElement.remove();
};

これまでの結果:👇

投稿結果の編集

休憩する!

休憩する

プロジェクト1を完了してくださった皆さん、おめでとうございます。さあ、ちょっと休憩してください。

CRUD操作を使用してToDoアプリを作成する方法

ToDoアプリを作ろう

To-Doアプリであるプロジェクト2の作成を始めましょう。

プロジェクトの設定

プロジェクトの設定

このプロジェクトでは、以下の手順に従います。

  • index.html、style.css、main.jsという名前の3つのファイルを作成します
  • JavaScriptファイルとCSSファイルをindex.htmlにリンクします
  • ライブサーバーを起動します

HTML

このスターターコードをHTMLファイル内に記述します:👇

<div class="app">
  <h4 class="mb-3">TODO App</h4>

  <div id="addNew" data-bs-toggle="modal" data-bs-target="#form">
    <span>Add New Task</span>
    <i class="fas fa-plus"></i>
  </div>
</div>

IDを持つdivaddNewは、モーダルを開くボタンです。ボタンにスパンが表示されます。これiはfont-awesomeのアイコンです。

ブートストラップを使用してモーダルを作成します。モーダルを使用して新しいタスクを追加します。そのために、headタグ内にブートストラップCDNリンクを追加します。👇

<link
  href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
  rel="stylesheet"
  integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
  crossorigin="anonymous"
/>

<script
  src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
  integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
  crossorigin="anonymous"
></script>

作成されたタスクを確認するには、クラス名アプリを使用したdiv内で、idタスクを使用したdivを使用します。👇

<h5 class="text-center my-3">Tasks</h5>

<div id="tasks"></div>

プロジェクトでフォントを使用するには、headタグ内にfont-awesomeCDNを挿入します👇

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" />

ブートストラップモーダルからの以下のコードをコピーして貼り付けます。3つの入力フィールドと送信ボタンを備えたフォームがあります。必要に応じて、検索バーに「モーダル」と入力して、BootstrapのWebサイトを検索できます。

<!-- Modal -->
<form
  class="modal fade"
  id="form"
  tabindex="-1"
  aria-labelledby="exampleModalLabel"
  aria-hidden="true"
>
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">Add New Task</h5>
        <button
          type="button"
          class="btn-close"
          data-bs-dismiss="modal"
          aria-label="Close"
        ></button>
      </div>
      <div class="modal-body">
        <p>Task Title</p>
        <input type="text" class="form-control" name="" id="textInput" />
        <div id="msg"></div>
        <br />
        <p>Due Date</p>
        <input type="date" class="form-control" name="" id="dateInput" />
        <br />
        <p>Description</p>
        <textarea
          name=""
          class="form-control"
          id="textarea"
          cols="30"
          rows="5"
        ></textarea>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
          Close
        </button>
        <button type="submit" id="add" class="btn btn-primary">Add</button>
      </div>
    </div>
  </div>
</form>

これまでの結果:👇

HTMLファイルの設定

HTMLファイルの設定は完了です。CSSを始めましょう。

CSS

css部分を追加する

これらのスタイルを本文に追加して、アプリを画面の正確な中央に配置できるようにします。

body {
  font-family: sans-serif;
  margin: 0 50px;
  background-color: #e5e5e5;
  overflow: hidden;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}

classnameアプリでdivのスタイルを設定しましょう。👇

.app {
  background-color: #fff;
  width: 300px;
  height: 500px;
  border: 5px solid #abcea1;
  border-radius: 8px;
  padding: 15px;
}

これまでの結果:👇

アプリのスタイル

それでは、idを使用してボタンのスタイルを設定しましょうaddNew。👇

#addNew {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background-color: rgba(171, 206, 161, 0.35);
  padding: 5px 10px;
  border-radius: 5px;
  cursor: pointer;
}
.fa-plus {
  background-color: #abcea1;
  padding: 3px;
  border-radius: 3px;
}

これまでの結果:👇

新しいタスクボタンを追加

ボタンをクリックすると、モーダルが次のようにポップアップ表示されます:👇

モーダルポップ

JSを追加する

JavaScriptを追加する

JavaScriptファイルで、まず、使用する必要のあるHTMLからすべてのセレクターを選択します。👇

let form = document.getElementById("form");
let textInput = document.getElementById("textInput");
let dateInput = document.getElementById("dateInput");
let textarea = document.getElementById("textarea");
let msg = document.getElementById("msg");
let tasks = document.getElementById("tasks");
let add = document.getElementById("add");

フォームの検証

ユーザーに空白の入力フィールドを送信させることはできません。したがって、入力フィールドを検証する必要があります。👇

form.addEventListener("submit", (e) => {
  e.preventDefault();
  formValidation();
});

let formValidation = () => {
  if (textInput.value === "") {
    console.log("failure");
    msg.innerHTML = "Task cannot be blank";
  } else {
    console.log("success");
    msg.innerHTML = "";
  }
};

また、CSS内に次の行を追加します。

#msg {
  color: red;
}

これまでの結果:👇

画像の説明

ご覧のとおり、検証は機能しています。JavaScriptコードでは、ユーザーが空白の入力フィールドを送信することはできません。そうしないと、エラーメッセージが表示されます。

データを収集してローカルストレージを使用する方法

ユーザーが書き込んだ入力が何であれ、それらを収集してローカルストレージに保存する必要があります。

まず、という名前の関数とという名前acceptDataの配列を使用して、入力フィールドからデータを収集しますdata。次に、次のようにローカルストレージ内にプッシュします:👇

let data = [];

let acceptData = () => {
  data.push({
    text: textInput.value,
    date: dateInput.value,
    description: textarea.value,
  });

  localStorage.setItem("data", JSON.stringify(data));

  console.log(data);
};

acceptDataまた、フォーム検証のelseステートメント内で関数を呼び出さない限り、これは機能しないことに注意してください。ここに従ってください:👇

let formValidation = () => {

  // Other codes are here
   else {

    // Other codes are here

    acceptData();
  }
};

モーダルが自動的に閉じないことに気付いたかもしれません。これを解決するには、フォーム検証のelseステートメント内にこの小さな関数を記述します。👇

let formValidation = () => {

  // Other codes are here
   else {

    // Other codes are here

    acceptData();
    add.setAttribute("data-bs-dismiss", "modal");
    add.click();

    (() => {
      add.setAttribute("data-bs-dismiss", "");
    })();
  }
};

Chrome開発ツールを開く場合は、アプリケーションに移動してローカルストレージを開きます。あなたはこの結果を見ることができます:👇

ローカルストレージの結果

新しいタスクを作成する方法

新しいタスクを作成するには、関数を作成し、テンプレートリテラルを使用してHTML要素を作成し、マップを使用してユーザーから収集したデータをテンプレート内にプッシュする必要があります。ここに従ってください:👇

let createTasks = () => {
  tasks.innerHTML = "";
  data.map((x, y) => {
    return (tasks.innerHTML += `
    <div id=${y}>
          <span class="fw-bold">${x.text}</span>
          <span class="small text-secondary">${x.date}</span>
          <p>${x.description}</p>
  
          <span class="options">
            <i onClick= "editTask(this)" data-bs-toggle="modal" data-bs-target="#form" class="fas fa-edit"></i>
            <i onClick ="deleteTask(this);createTasks()" class="fas fa-trash-alt"></i>
          </span>
        </div>
    `);
  });

  resetForm();
};

また、次のacceptDataように、関数内で呼び出さない限り、関数は実行されないことに注意してください。👇

let acceptData = () => {
  // Other codes are here

  createTasks();
};

ユーザーからのデータの収集と受け入れが完了したら、入力フィールドをクリアする必要があります。そのために、と呼ばれる関数を作成しますresetForm。フォローする:👇

let resetForm = () => {
  textInput.value = "";
  dateInput.value = "";
  textarea.value = "";
};

これまでの結果:👇

タスクカードの追加

ご覧のとおり、このカードにはスタイルはありません。いくつかのスタイルを追加しましょう:👇

#tasks {
  display: grid;
  grid-template-columns: 1fr;
  gap: 14px;
}

#tasks div {
  border: 3px solid #abcea1;
  background-color: #e2eede;
  border-radius: 6px;
  padding: 5px;
  display: grid;
  gap: 4px;
}

次のコードで編集ボタンと削除ボタンのスタイルを設定します:👇

#tasks div .options {
  justify-self: center;
  display: flex;
  gap: 20px;
}

#tasks div .options i {
  cursor: pointer;
}

これまでの結果:👇

スタイルカードテンプレート

タスクを削除する機能

ここを注意深く見てください。関数内に3行のコードを追加しました。

  • 最初の行は、画面からHTML要素を削除します。
  • 2行目は、ターゲットのタスクをデータ配列から削除します。
  • 3行目は、ローカルストレージを新しいデータで更新します。
let deleteTask = (e) => {
  e.parentElement.parentElement.remove();

  data.splice(e.parentElement.parentElement.id, 1);

  localStorage.setItem("data", JSON.stringify(data));

  console.log(data);
};

次に、ダミータスクを作成し、それを削除してみます。これまでの結果は次のようになります:👇

画像の説明

タスクを編集する機能

ここを注意深く見てください。関数内に5行のコードを追加しました。

  • 1行目は、編集するために選択したタスクを対象としています
  • 2行目、3行目、および4行目は、編集するために選択したタスクの値[タスク、日付、説明]を対象としています。
  • 5行目では、削除機能を実行して、選択したデータをローカルストレージ、HTML要素、およびデータ配列の両方から削除しています。
let editTask = (e) => {
  let selectedTask = e.parentElement.parentElement;

  textInput.value = selectedTask.children[0].innerHTML;
  dateInput.value = selectedTask.children[1].innerHTML;
  textarea.value = selectedTask.children[2].innerHTML;

  deleteTask(e);
};

次に、ダミータスクを作成して編集してみます。これまでの結果:👇

タスクの編集

ローカルストレージからデータを取得する方法

ページを更新すると、すべてのデータが失われていることに気付くでしょう。この問題を解決するために、IIFE(即時呼び出し関数式)を実行して、ローカルストレージからデータを取得します。フォローする:👇

(() => {
  data = JSON.parse(localStorage.getItem("data")) || [];
  console.log(data);
  createTasks();
})();

これで、ページを更新してもデータが表示されます。

結論

おめでとう

このチュートリアルを正常に完了しました。おめでとうございます。CRUD操作を使用してToDoリストアプリケーションを作成する方法を学習しました。これで、このチュートリアルを使用して独自のCRUDアプリケーションを作成できます。

これが最後まで読むためのあなたのメダルです。❤️

ソース:https ://www.freecodecamp.org/news/learn-crud-operations-in-javascript-by-building-todo-app/

#javascript #crud #operator #todoapp 

TODO APPを構築して、JavaScriptでのCRUD操作を学ぶ

Cómo Hacer Operaciones CRUD En JavaScript Creando Una Aplicación Todo

Hoy vamos a aprender cómo hacer operaciones CRUD en JavaScript creando una aplicación Todo. Empecemos 🔥

Esta es la aplicación que estamos haciendo hoy:

Aplicación que estamos haciendo hoy

¿Qué es CRUD?

descripción de la imagen

CRUD significa -

  • c: crear
  • R: Leer
  • U: Actualizar
  • D: Eliminar

CRUD de forma completa

CRUD es un tipo de mecanismo que le permite crear datos, leer datos, editarlos y eliminar esos datos. En nuestro caso, vamos a crear una aplicación Todo, por lo que tendremos 4 opciones para crear tareas, leer tareas, actualizar tareas o eliminar tareas.

Comprender los principios CRUD

Antes de comenzar el tutorial, primero, comprendamos los principios CRUD. Para eso, creemos una aplicación de redes sociales muy, muy simple.

Proyecto de aplicación de redes sociales

Configuración

Configuración del proyecto

Para este proyecto, seguiremos los siguientes pasos:

  • Cree 3 archivos llamados index.html, style.css y main.js
  • Vincule el archivo JavaScript y CSS a index.html
  • Inicie su servidor en vivo

HTML

Dentro de la etiqueta del cuerpo, crea un div con un nombre de clase .container. Ahí tendremos 2 secciones, .lefty .right👇

<body>
  <h1>Social Media App</h1>
  <div class="container">

    <div class="left"></div>
    <div class="right"></div>

  </div>
</body>

En el lado izquierdo, crearemos nuestras publicaciones. En el lado derecho, podemos ver, actualizar y eliminar nuestras publicaciones. Ahora, crea un formulario dentro de la etiqueta div .left 👇

<div class="left">
  <form id="form">
    <label for="post"> Write your post here</label>
    <br><br>
    <textarea name="post" id="input" cols="30" rows="10"></textarea>
    <br> <br>
    <div id="msg"></div>
    <button type="submit">Post</button>
  </form>
</div>

Escribe este código dentro del HTML para que podamos mostrar nuestra publicación en el lado derecho 👇

<div class="right">
  <h3>Your posts here</h3>
  <div id="posts"></div>
</div>

A continuación, insertaremos el CDN font-awesome dentro de la etiqueta de encabezado para usar sus fuentes en nuestro proyecto 👇

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" />

Ahora, vamos a hacer algunas publicaciones de muestra con íconos de eliminar y editar. Escribe este código dentro del div con el id #posts: 👇

<div id="posts">
  <div>
    <p>Hello world post 1</p>
    <span class="options">
      <i class="fas fa-edit"></i>
      <i class="fas fa-trash-alt"></i>
    </span>
  </div>

  <div >
    <p>Hello world post 2</p>
    <span class="options">
      <i class="fas fa-edit"></i>
      <i class="fas fa-trash-alt"></i>
    </span>
  </div>
</div>

El resultado hasta ahora se ve así:

Resultado de marcado HTML

CSS

Agregar CSS para el proyecto 1

Mantengámoslo simple. Escribe estos estilos dentro de tu hoja de estilo: 👇

body {
  font-family: sans-serif;
  margin: 0 50px;
}

.container {
  display: flex;
  gap: 50px;
}

#posts {
  width: 400px;
}

i {
  cursor: pointer;
}

Ahora, escribe estos estilos para los íconos de opción y div posteriores: 👇

#posts div {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.options {
  display: flex;
  gap: 25px;
}

#msg {
  color: red;
}

Los resultados hasta ahora se ven así: 👇

El resultado después de agregar el proyecto de parte css 1

Parte de JavaScript

Comenzando la parte de javascript

De acuerdo con este diagrama de flujo, seguiremos adelante con el proyecto. No te preocupes, te explicaré todo en el camino. 👇

diagrama de flujo

Validación de formulario

Primero, apuntemos a todos los selectores de ID del HTML en JavaScript. Así: 👇

let form = document.getElementById("form");
let input = document.getElementById("input");
let msg = document.getElementById("msg");
let posts = document.getElementById("posts");

Luego, cree un detector de eventos de envío para el formulario para que pueda evitar el comportamiento predeterminado de nuestra aplicación. Al mismo tiempo, crearemos una función llamada formValidation. 👇

form.addEventListener("submit", (e) => {
  e.preventDefault();
  console.log("button clicked");

  formValidation();
});

let formValidation = () => {};

Ahora, vamos a hacer una declaración if else dentro de nuestra formValidationfunción. Esto nos ayudará a evitar que los usuarios envíen campos de entrada en blanco. 👇

let formValidation = () => {
  if (input.value === "") {
    msg.innerHTML = "Post cannot be blank";
    console.log("failure");
  } else {
    console.log("successs");
    msg.innerHTML = "";
  }
};

Aquí está el resultado hasta ahora: 👇

7sb8faq21j5dzy9vlswj

Como puede ver, también aparecerá un mensaje si el usuario intenta enviar el formulario en blanco.

Cómo aceptar datos de campos de entrada

Cualesquiera que sean los datos que obtengamos de los campos de entrada, los almacenaremos en un objeto. Vamos a crear un objeto llamado data. Y crea una función llamada acceptData: 👇

let data = {};

let acceptData = () => {};

La idea principal es que, usando la función, recopilamos datos de las entradas y los almacenamos en nuestro objeto llamado data. Ahora terminemos de construir nuestra acceptDatafunción.

let acceptData = () => {
  data["text"] = input.value;
  console.log(data);
};

Además, necesitamos que la acceptDatafunción funcione cuando el usuario haga clic en el botón Enviar. Para eso, activaremos esta función en la instrucción else de nuestra formValidationfunción. 👇

let formValidation = () => {
  if (input.value === "") {
    // Other codes are here
  } else {
    // Other codes are here
    acceptData();
  }
};

Cuando ingresamos datos y enviamos el formulario, en la consola podemos ver un objeto que contiene los valores de entrada de nuestro usuario. Así: 👇

resultado hasta ahora en la consola

Cómo crear publicaciones usando literales de plantilla de JavaScript

Para publicar nuestros datos de entrada en el lado derecho, necesitamos crear un elemento div y agregarlo al div de publicaciones. Primero, creemos una función y escribamos estas líneas: 👇

let createPost = () => {
  posts.innerHTML += ``;
};

Los acentos graves son literales de plantilla. Funcionará como una plantilla para nosotros. Aquí, necesitamos 3 cosas: un div principal, la entrada en sí y el div de opciones que lleva los íconos de edición y eliminación. Terminemos nuestra función 👇

let createPost = () => {
  posts.innerHTML += `
  <div>
    <p>${data.text}</p>
    <span class="options">
      <i onClick="editPost(this)" class="fas fa-edit"></i>
      <i onClick="deletePost(this)" class="fas fa-trash-alt"></i>
    </span>
  </div>
  `;
  input.value = "";
};

En nuestra acceptdatafunción, activaremos nuestra createPostfunción. Así: 👇

let acceptData = () => {
  // Other codes are here

  createPost();
};

El resultado hasta ahora: 👇

resultado hasta ahora

Hasta ahora todo bien chicos, casi hemos terminado con el proyecto 1.

Hasta ahora, todo bien

Cómo eliminar una publicación

Para eliminar una publicación, en primer lugar, creemos una función dentro de nuestro archivo javascript:

let deletePost = (e) => {};

A continuación, activamos esta deletePostfunción dentro de todos nuestros íconos de eliminación usando un atributo onClick. Escribirá estas líneas en HTML y en el literal de la plantilla. 👇

<i onClick="deletePost(this)" class="fas fa-trash-alt"></i>

La thispalabra clave se referirá al elemento que disparó el evento. en nuestro caso, el thisse refiere al botón eliminar.

Mire con cuidado, el padre del botón Eliminar es el tramo con opciones de nombre de clase. El padre del lapso es el div. Entonces, escribimos parentElementdos veces para que podamos saltar del ícono de eliminar al div y apuntarlo directamente para eliminarlo.

Terminemos nuestra función. 👇

let deletePost = (e) => {
  e.parentElement.parentElement.remove();
};

El resultado hasta ahora: 👇

eliminar el resultado de una publicación

Cómo editar una publicación

Para editar una publicación, en primer lugar, creemos una función dentro de nuestro archivo JavaScript:

let editPost = (e) => {};

A continuación, activamos esta editPostfunción dentro de todos nuestros íconos de edición usando un atributo onClick. Escribirá estas líneas en HTML y en el literal de la plantilla. 👇

<i onClick="editPost(this)" class="fas fa-edit"></i>

La thispalabra clave se referirá al elemento que disparó el evento. En nuestro caso, el thisse refiere al botón editar.

Mire con cuidado, el padre del botón de edición es el tramo con opciones de nombre de clase. El padre del lapso es el div. Entonces, escribimos parentElementdos veces para que podamos saltar del ícono de edición al div y apuntarlo directamente para eliminarlo.

Luego, cualquier dato que esté dentro de la publicación, lo traemos de vuelta al campo de entrada para editarlo.

Terminemos nuestra función. 👇

let editPost = (e) => {
  input.value = e.parentElement.previousElementSibling.innerHTML;
  e.parentElement.parentElement.remove();
};

El resultado hasta ahora: 👇

Editar el resultado de una publicación

¡Tomar un descanso!

Tomar un descanso

Felicitaciones a todos por completar el proyecto 1. Ahora, ¡tómense un pequeño descanso!

Cómo hacer una aplicación de tareas pendientes usando operaciones CRUD

Hagamos una aplicación de tareas

Comencemos a hacer el proyecto 2, que es una aplicación To-Do.

Configuración del proyecto

configuración del proyecto

Para este proyecto, seguiremos los siguientes pasos:

  • Cree 3 archivos llamados index.html, style.css y main.js
  • Vincule los archivos JavaScript y CSS a index.html
  • Inicie nuestro servidor en vivo

HTML

Escribe este código de inicio dentro del archivo HTML: 👇

<div class="app">
  <h4 class="mb-3">TODO App</h4>

  <div id="addNew" data-bs-toggle="modal" data-bs-target="#form">
    <span>Add New Task</span>
    <i class="fas fa-plus"></i>
  </div>
</div>

El div con una identificación addNewes el botón que abrirá el modal. El intervalo se mostrará en el botón. El ies el ícono de font-awesome.

Vamos a usar bootstrap para hacer nuestro modal. Usaremos el modal para agregar nuevas tareas. Para eso, agregue el enlace CDN de arranque dentro de la etiqueta principal. 👇

<link
  href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
  rel="stylesheet"
  integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
  crossorigin="anonymous"
/>

<script
  src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
  integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
  crossorigin="anonymous"
></script>

Para ver las tareas creadas, usaremos un div con una tarea de identificación, dentro del div con la aplicación de nombre de clase. 👇

<h5 class="text-center my-3">Tasks</h5>

<div id="tasks"></div>

Inserte el CDN font-awesome dentro de la etiqueta principal para usar fuentes en nuestro proyecto 👇

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" />

Copie y pegue el código a continuación que proviene del modal de arranque. Lleva un formulario con 3 campos de entrada y un botón de envío. Si lo desea, puede buscar en el sitio web de Bootstrap escribiendo 'modal' en la barra de búsqueda.

<!-- Modal -->
<form
  class="modal fade"
  id="form"
  tabindex="-1"
  aria-labelledby="exampleModalLabel"
  aria-hidden="true"
>
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">Add New Task</h5>
        <button
          type="button"
          class="btn-close"
          data-bs-dismiss="modal"
          aria-label="Close"
        ></button>
      </div>
      <div class="modal-body">
        <p>Task Title</p>
        <input type="text" class="form-control" name="" id="textInput" />
        <div id="msg"></div>
        <br />
        <p>Due Date</p>
        <input type="date" class="form-control" name="" id="dateInput" />
        <br />
        <p>Description</p>
        <textarea
          name=""
          class="form-control"
          id="textarea"
          cols="30"
          rows="5"
        ></textarea>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
          Close
        </button>
        <button type="submit" id="add" class="btn btn-primary">Add</button>
      </div>
    </div>
  </div>
</form>

El resultado hasta ahora: 👇

Configuración del archivo HTML

Hemos terminado con la configuración del archivo HTML. Comencemos el CSS.

CSS

Agregar la parte css

Agregue estos estilos en el cuerpo para que podamos mantener nuestra aplicación en el centro exacto de la pantalla.

body {
  font-family: sans-serif;
  margin: 0 50px;
  background-color: #e5e5e5;
  overflow: hidden;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}

Apliquemos estilo al div con la aplicación classname. 👇

.app {
  background-color: #fff;
  width: 300px;
  height: 500px;
  border: 5px solid #abcea1;
  border-radius: 8px;
  padding: 15px;
}

El resultado hasta ahora: 👇

Estilos de aplicaciones

Ahora, diseñemos el botón con el id addNew. 👇

#addNew {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background-color: rgba(171, 206, 161, 0.35);
  padding: 5px 10px;
  border-radius: 5px;
  cursor: pointer;
}
.fa-plus {
  background-color: #abcea1;
  padding: 3px;
  border-radius: 3px;
}

El resultado hasta ahora: 👇

Agregar nueva tarea Botón

Si hace clic en el botón, el modal aparece así: 👇

Estallidos modales

Agregar el JS

Agregar el JavaScript

En el archivo JavaScript, en primer lugar, seleccione todos los selectores del HTML que necesitamos usar. 👇

let form = document.getElementById("form");
let textInput = document.getElementById("textInput");
let dateInput = document.getElementById("dateInput");
let textarea = document.getElementById("textarea");
let msg = document.getElementById("msg");
let tasks = document.getElementById("tasks");
let add = document.getElementById("add");

Validaciones de formulario

No podemos permitir que un usuario envíe campos de entrada en blanco. Entonces, necesitamos validar los campos de entrada. 👇

form.addEventListener("submit", (e) => {
  e.preventDefault();
  formValidation();
});

let formValidation = () => {
  if (textInput.value === "") {
    console.log("failure");
    msg.innerHTML = "Task cannot be blank";
  } else {
    console.log("success");
    msg.innerHTML = "";
  }
};

Además, agregue esta línea dentro del CSS:

#msg {
  color: red;
}

El resultado hasta ahora: 👇

descripción de la imagen

Como puede ver, la validación está funcionando. El código JavaScript no permite que el usuario envíe campos de entrada en blanco; de lo contrario, verá un mensaje de error.

Cómo recopilar datos y utilizar el almacenamiento local

Independientemente de las entradas que escriba el usuario, debemos recopilarlas y almacenarlas en el almacenamiento local.

Primero, recopilamos los datos de los campos de entrada, usando la función named acceptDatay una matriz llamada data. Luego los empujamos dentro del almacenamiento local así: 👇

let data = [];

let acceptData = () => {
  data.push({
    text: textInput.value,
    date: dateInput.value,
    description: textarea.value,
  });

  localStorage.setItem("data", JSON.stringify(data));

  console.log(data);
};

También tenga en cuenta que esto nunca funcionará a menos que invoque la función acceptDatadentro de la declaración else de la validación del formulario. Síguenos aquí: 👇

let formValidation = () => {

  // Other codes are here
   else {

    // Other codes are here

    acceptData();
  }
};

Es posible que haya notado que el modal no se cierra automáticamente. Para resolver esto, escribe esta pequeña función dentro de la instrucción else de la validación del formulario: 👇

let formValidation = () => {

  // Other codes are here
   else {

    // Other codes are here

    acceptData();
    add.setAttribute("data-bs-dismiss", "modal");
    add.click();

    (() => {
      add.setAttribute("data-bs-dismiss", "");
    })();
  }
};

Si abre las herramientas de desarrollo de Chrome, vaya a la aplicación y abra el almacenamiento local. Puedes ver este resultado: 👇

Resultado de almacenamiento local

Cómo crear nuevas tareas

Para crear una nueva tarea, necesitamos crear una función, usar literales de plantilla para crear los elementos HTML y usar un mapa para insertar los datos recopilados del usuario dentro de la plantilla. Síguenos aquí: 👇

let createTasks = () => {
  tasks.innerHTML = "";
  data.map((x, y) => {
    return (tasks.innerHTML += `
    <div id=${y}>
          <span class="fw-bold">${x.text}</span>
          <span class="small text-secondary">${x.date}</span>
          <p>${x.description}</p>
  
          <span class="options">
            <i onClick= "editTask(this)" data-bs-toggle="modal" data-bs-target="#form" class="fas fa-edit"></i>
            <i onClick ="deleteTask(this);createTasks()" class="fas fa-trash-alt"></i>
          </span>
        </div>
    `);
  });

  resetForm();
};

También tenga en cuenta que la función nunca se ejecutará a menos que la invoque dentro de la acceptDatafunción, así: 👇

let acceptData = () => {
  // Other codes are here

  createTasks();
};

Una vez que hayamos terminado de recopilar y aceptar datos del usuario, debemos borrar los campos de entrada. Para eso creamos una función llamada resetForm. Síguenos: 👇

let resetForm = () => {
  textInput.value = "";
  dateInput.value = "";
  textarea.value = "";
};

El resultado hasta ahora: 👇

Adición de tarjetas de tareas

Como puede ver, no hay estilos con la tarjeta. Agreguemos algunos estilos: 👇

#tasks {
  display: grid;
  grid-template-columns: 1fr;
  gap: 14px;
}

#tasks div {
  border: 3px solid #abcea1;
  background-color: #e2eede;
  border-radius: 6px;
  padding: 5px;
  display: grid;
  gap: 4px;
}

Dale estilo a los botones de editar y eliminar con este código: 👇

#tasks div .options {
  justify-self: center;
  display: flex;
  gap: 20px;
}

#tasks div .options i {
  cursor: pointer;
}

El resultado hasta ahora: 👇

Plantillas de tarjetas de estilos

Función para eliminar una tarea

Mire aquí cuidadosamente, agregué 3 líneas de código dentro de la función.

  • La primera línea eliminará el elemento HTML de la pantalla,
  • la segunda línea eliminará la tarea objetivo de la matriz de datos,
  • y la tercera línea actualizará el almacenamiento local con los nuevos datos.
let deleteTask = (e) => {
  e.parentElement.parentElement.remove();

  data.splice(e.parentElement.parentElement.id, 1);

  localStorage.setItem("data", JSON.stringify(data));

  console.log(data);
};

Ahora cree una tarea ficticia e intente eliminarla. El resultado hasta ahora se ve así: 👇

descripción de la imagen

Función para editar tareas

Mire aquí cuidadosamente, agregué 5 líneas de código dentro de la función.

  • La línea 1 apunta a la tarea que seleccionamos para editar
  • Las líneas 2, 3 y 4 apuntan a los valores [tarea, fecha, descripción] de la tarea que seleccionamos para editar
  • la línea 5 está ejecutando la función de eliminación para eliminar los datos seleccionados tanto del almacenamiento local, el elemento HTML y la matriz de datos.
let editTask = (e) => {
  let selectedTask = e.parentElement.parentElement;

  textInput.value = selectedTask.children[0].innerHTML;
  dateInput.value = selectedTask.children[1].innerHTML;
  textarea.value = selectedTask.children[2].innerHTML;

  deleteTask(e);
};

Ahora, intente crear una tarea ficticia y edítela. El resultado hasta ahora: 👇

Edición de una tarea

Cómo obtener datos del almacenamiento local

Si actualiza la página, notará que todos sus datos han desaparecido. Para resolver ese problema, ejecutamos un IIFE (expresión de función invocada inmediatamente) para recuperar los datos del almacenamiento local. Síguenos: 👇

(() => {
  data = JSON.parse(localStorage.getItem("data")) || [];
  console.log(data);
  createTasks();
})();

Ahora los datos aparecerán incluso si actualiza la página.

Conclusión

Felicidades

Felicitaciones por completar con éxito este tutorial. Ha aprendido a crear una aplicación de lista de tareas mediante operaciones CRUD. Ahora, puede crear su propia aplicación CRUD usando este tutorial.

Aquí está tu medalla por leer hasta el final. ❤️

Fuente: https://www.freecodecamp.org/news/learn-crud-operations-in-javascript-by-building-todo-app/ 

#javascript #crud #operator #todoapp

Cómo Hacer Operaciones CRUD En JavaScript Creando Una Aplicación Todo

Windows Vs MacOS Vs Linux – Manual Del Sistema Operativo

¡Hola a todos!

En este artículo, daré una breve introducción a los sistemas operativos y compararé los tres principales sistemas operativos que existen hoy en día.

Primero vamos a repasar qué es un SO y un poco de historia sobre ellos. Luego, repasaremos las principales características y diferencias de los sistemas operativos más populares (Windows, Mac y GNU/Linux).

La idea aquí es explorar su historia, cómo y por quién fueron desarrollados, sus modelos de negocio y sus pros y contras. Esto le dará una mejor idea de cómo funcionan y cuál elegir.

Voy a compartir hechos, así como mis opiniones personales sobre este tema. Así que tenga en cuenta que algunas de las cosas que menciono aquí se basarán en mi propia experiencia y análisis del tema.

También proporcionaré una gran cantidad de artículos/videos adicionales que puede consultar en caso de que desee sumergirse en un tema en particular.

Sin más preámbulos, ¡vamos!

¿Qué es un sistema operativo?

Según Wikipedia ,

"Un sistema operativo (SO) es un sistema de software que administra el hardware de la computadora, los recursos de software y proporciona servicios comunes para los programas de computadora".

Puede pensar en un sistema operativo como un programa "intermediario" que se interpone entre su computadora y todos los demás programas que ejecuta en ella. Gestionará tareas básicas cruciales, como la gestión de archivos, la gestión de memoria, la gestión de procesos, la gestión de entrada y salida y el control de dispositivos periféricos.

Los sistemas operativos se crearon para simplificar el uso de las computadoras. Hoy en día, cualquier programa dado puede preocuparse solo por ejecutar sus funciones principales y dejar todas las funcionalidades básicas del sistema al sistema operativo. Pero las cosas no siempre fueron así...

Un poco de historia de los sistemas operativos

an9zgv0_700b

En los viejos tiempos (1940's-50's) los programas se escribieron para ejecutarse en máquinas específicas. Eso significa que un programa podría ejecutarse en un solo modelo de computadora.

Si quisiera ejecutar el mismo programa en un modelo de computadora diferente, los programadores tendrían que volver a escribir todo el programa porque el hardware estaba configurado de manera diferente. No había ninguna capa de abstracción entre el programa en ejecución y el hardware real.

Comentario al margen: ¿Alguna vez te detuviste a pensar en el trabajo de un programador en esos días? ¡Los programas se escribieron en tarjetas perforadas! =O
Simplemente me sorprende cada vez que pienso en ello... Es increíble lo bajo que estaban las cosas en ese momento y el progreso que la tecnología ha logrado gracias a esos primeros programadores.

837755a86e841b0452e12f0786128e02

En la década de 1960, gigantes de la industria como IBM y AT&T comenzaron a trabajar en sistemas operativos que pudieran actuar como una capa de abstracción entre el hardware y el software, lo que simplificaría la implementación de nuevos programas.

El más notorio de estos proyectos fue Unix , que era un sistema operativo desarrollado en los laboratorios Bell de AT&T por los desarrolladores Ken Thompson (que actualmente está trabajando en el desarrollo del lenguaje de programación Go) y Dennis Ritchie (quien también creó el lenguaje de programación C). leyendas, sí).

ken-thompson-dennis-ritchie-111013

Unix tuvo un gran éxito e inspiró la creación de muchos otros sistemas operativos con características muy similares. Los posteriores tuvieron una gran influencia en GNU/Linux y MacOs, que revisaremos en un segundo.

En la década de 1980, el rendimiento, la accesibilidad, el tamaño y el precio de las computadoras habían mejorado hasta el punto en que el público en general podía comprarlas y usarlas para tareas personales. Esto hizo que los sistemas operativos pasaran de funciones corporativas específicas a uso general. Y esto nos lleva a la era moderna...

Si está interesado en una explicación más detallada de cómo funcionan los sistemas operativos y su historia, aquí hay un excelente video al respecto. Este canal también tiene una increíble serie de cursos intensivos sobre informática, ¡definitivamente lo recomiendo! ;)

Los tres sistemas operativos principales

En la actualidad, cuando se habla de computadoras personales de escritorio/portátiles, los tres sistemas operativos más utilizados son Microsoft Windows (con una participación de mercado de alrededor del 80 %), Apple MacOs (con una participación de mercado de alrededor del 15 %) y los sistemas operativos basados ​​en GNU/Linux ( con alrededor del 3% de cuota de mercado).

En cuanto a los servidores, alrededor del 80% ejecuta GNU/Linux y el 20% ejecuta Windows. Y hablando de dispositivos móviles, alrededor del 75 % ejecuta Android (que usa el kernel de Linux) y el 25 % ejecuta IO (que es el sistema operativo móvil de Apple).

Vamos a revisar brevemente cada uno de ellos individualmente y luego compararlos todos para identificar sus diferencias.

Sistema Operativo Windows

516094c7ed4e0--1-

El antepasado de Windows es MS-DOS , un sistema operativo basado en texto que Microsoft lanzó en 1981.

MS-DOS fue desarrollado para ser compatible con las PC de IBM y tuvo mucho éxito. Pero para hacerlo más accesible al público en general, necesitaba una GUI, y eso es lo que Microsoft envió en 1985 con Windows 1.0.

Desde entonces, Windows ha lanzado muchas versiones, como 95, 98, XP, Vista, etc., y se ha convertido en el sistema operativo más utilizado en todo el mundo.

La accesibilidad de Windows y el hecho de que viene preinstalado en la mayoría de las computadoras personales (gracias a acuerdos comerciales) han hecho de este sistema operativo el más popular hasta el día de hoy.

Aquí hay un video genial que resume la historia de Windows en solo 3 minutos.

Y si te interesa saber más sobre la historia de Microsoft, aquí tienes otro vídeo genial al respecto .

En cuanto a su modelo de negocio, diría que la estrategia de Windows es inundar el mercado y hacer que su sistema sea lo más accesible y fácil de usar posible. Su principal cliente objetivo es el usuario general, por lo que no se le da mucha importancia a la personalización, la seguridad o el rendimiento.

Windows es solo el sistema operativo predeterminado para la mayoría de las personas. Es el primero que conocen y le permite al usuario ejecutar fácilmente las tareas diarias (navegación por Internet, juegos, trabajo de oficina) sin mucha configuración.

Windows es una pieza de software privada, lo que significa que su código fuente no está disponible públicamente. Solo Microsoft tiene acceso a él.

Al principio, los usuarios tenían que pagar si querían comprar una copia del sistema operativo Windows o actualizar su versión de Windows. Pero con sus últimos lanzamientos, Windows ha adoptado un modelo freemium. Bajo este modelo de negocio, el usuario puede acceder a la mayoría de las funcionalidades del software de forma gratuita y solo necesita pagar para acceder a determinadas funciones.

La clave para entender este cambio es entender que Microsoft tiene una cartera de negocios enormemente diversificada (Xbox - en juegos, Azzure - en plataformas en la nube, Linkedin - en redes sociales, Bing - en motores de búsqueda, Github... pocos). Al hacer que Windows sea gratuito, siguen inundando el mercado y hacen que sea aún más fácil para las personas adoptarlo como sistema operativo predeterminado.

Otra cosa a tener en cuenta es que Windows muestra anuncios dentro del sistema operativo. Por lo tanto, también se puede pensar como una plataforma publicitaria.

Otro video genial que explica este movimiento aquí .

Y un ejemplo bizarro/divertido/pequeño-aterrador del estilo de marketing de la vieja escuela de Microsoft .

Mac OS

¿Cómo-conecto-una-memoria-USB-en-este--2000-Macbook-meme-7242

MacOS (anteriormente llamado OS X) es una línea de sistemas operativos creados por Apple. Viene preinstalado en todas las computadoras Macintosh o Mac. La primera versión se lanzó en 1984 y fue el primer sistema operativo para computadoras personales en venir con una GUI incorporada.

MacOS está construido sobre un sistema operativo similar a UNIX, por lo que este MacOS comparte muchas características comunes con los derivados de GNU/Linux.

En mi opinión, el modelo de negocio de Apple se basa principalmente en la diferenciación y la exclusividad. A diferencia de Microsoft, Apple fabrica tanto el hardware como el software de sus productos, y el software de Apple solo se ejecuta en sus propias máquinas.

Apple se ha posicionado como un fabricante de primer nivel dentro del mercado tecnológico, con el objetivo de ofrecer a sus clientes hardware y software de alta calidad, a un precio considerablemente más alto que la mayoría de la competencia.

La exclusividad también se promueve como un beneficio para los usuarios, vendiendo la idea de ser parte de un grupo selecto de personas cuando se posee un producto Apple.

El hecho de que no pueda ejecutar ningún software que desee en su hardware, y que no pueda instalar su software en ningún otro lugar que no sea una máquina Mac es parte de la misma idea. Necesitas comprar el paquete completo si quieres ser parte del grupo.

Apple fabrica la mayor parte de su software y hardware de manera diferente y muchas veces incompatible con otros. A diferencia de Microsoft, cuya idea es hacer que el producto esté lo más disponible y fácil de acceder posible, Apple tiene como objetivo hacer que sus productos sean de la mejor calidad pero caros e incompatibles con otro hardware.

Otro gran movimiento de marketing de Apple ha sido su capacidad para sacar provecho de las personalidades enormemente carismáticas e influyentes de personas como Steve Jobs . Han aprovechado su posición y trayectoria como líder de la industria, innovador y de alguna manera "rebelde", para traducir implícitamente esos mismos valores a sus productos.

Echa un vistazo a estos anuncios para saber a qué me refiero:

Si te interesa saber más sobre la historia de MacOS, aquí tienes un vídeo al respecto .

GNU/Linux

GNU/Linux es la base de muchos sistemas operativos de código abierto. A diferencia de los ejemplos que acabamos de ver, GNU/Linux no es un sistema operativo completo, sino un conjunto de programas/utilidades y un núcleo que comparten muchos sistemas operativos de código abierto.

Repasemos cada parte por separado.

GNU es una enorme colección de programas y utilidades que inició Richard Stallman .

EinHz4EWkAQYthk

El proyecto GNU se inició en 1983 con la idea de desarrollar un sistema operativo similar a UNIX gratuito (UNIX era propiedad de AT&T, por lo que no estaba disponible de forma gratuita). Stallman comenzó a desarrollar programas y utilidades necesarios para el sistema operativo, pero faltaba una pieza clave: el núcleo.

El kernel es el corazón de cualquier sistema operativo. Es la pieza de software que interactúa más de cerca con el hardware y el resto del sistema operativo se encuentra encima. El Kernel es responsable de las tareas de bajo nivel, como la gestión del disco, la gestión de la memoria, la gestión de tareas, etc.

En 1991, un estudiante de la universidad de Helsinki llamado Linus Torvalds comenzó a desarrollar un kernel para un sistema operativo similar a UNIX.

este-dedo-escribió-linux

En los años siguientes, ambos proyectos comenzaron a interactuar y se unieron para formar una base sólida que cualquier sistema operativo podría usar.

La clave aquí es que ambos proyectos son de código abierto y software completamente libre. Esto significa:

  • Cualquiera es libre de ejecutar el programa, para cualquier propósito.
  • Cualquiera es libre de estudiar cómo funciona el programa y cambiarlo para que haga lo que desee.
  • Cualquiera es libre de redistribuir copias del software original.
  • Cualquiera es libre de distribuir copias de versiones modificadas del software.

Para comprender mejor el movimiento del software libre, escuche esta charla TED de Richard .

Y luego mira a Richard hablar español y cantar una canción sobre software libre (te tiene que encantar este tipo...).

El enfoque que adoptaron Stallman y Torvalds en el desarrollo de GNU/Linux es radicalmente diferente a los ejemplos que hemos visto ya lo que la industria estaba acostumbrada hasta ese momento.

Liberar GNU/Linux no sólo fue lo correcto desde el punto de vista de sus desarrolladores, sino que también fue una elección excelente desde el punto de vista de la calidad del software. Esto se debe a que miles de desarrolladores y empresas de todo el mundo optan por colaborar de forma gratuita para mejorar el sistema.

Se sabe que algunas de las distribuciones de GNU/Linux son los sistemas operativos más seguros y estables que existen. Se utilizan en esferas clave como la banca, las finanzas, el gobierno y el ejército.

Gran parte de esto se debe al modelo de código abierto detrás de GNU/Linux, y que miles de personas en todo el mundo pueden revisar el código, corregir errores y proponer mejoras constantemente.

Estos dos videos de la fundación Linux explican cómo nació Linux y cómo funciona actualmente .

Como se mencionó, GNU/Linux sirve como base para muchos otros sistemas operativos. Estos sistemas operativos se denominan "distribuciones" o "distribuciones" dentro del mundo Linux. Todos tienen en común que están basados ​​en el mismo kernel y conjunto de utilidades. Pueden considerarse como "sabores" de Linux.

No hay mucha diferencia entre ciertas distribuciones, pero otras tienen distinciones que vale la pena mencionar. Repasemos rápidamente las distribuciones más utilizadas para entender mejor esto:

Debian

Debian es un sistema operativo que contiene solo software gratuito de código abierto. Debian se inició en 1993 y aún se mantiene fuerte y lanza nuevas versiones. Debian es conocido principalmente por su estabilidad y seguridad, lo que lo hace más conservador y "lento" cuando se trata de nuevas versiones.

ubuntu

Ubuntu es la distribución GNU/Linux más utilizada. Fue creado para tomar las partes principales de Debian y mejorarlas más rápidamente. También tiene un mayor enfoque en la facilidad de uso y la accesibilidad, lo que probablemente la convierte en la mejor opción para alguien que tenga experiencia en Windows o MacOS.

Ubuntu normalmente ofrece lanzamientos cada seis meses, con un lanzamiento LTS (soporte a largo plazo) más estable cada dos años. Ubuntu está a cargo de una empresa llamada Canonical .

Como

Mint es una distribución construida sobre Ubuntu. Originalmente, a muchos les encantó porque incluía códecs de medios y software propietario que Ubuntu no incluía.

Fedora

Fedora es una distribución que se enfoca fuertemente en el software libre. Fedora está patrocinado por una empresa llamada Red Hat , que a su vez es propiedad de IBM .

Red hat Enterprise Linux

Red Hat Enterprise Linux es una distribución comercial de Linux administrada por una empresa llamada Red Hat, que cotiza en Nasdaq. El sistema operativo se utiliza principalmente para servidores y corporaciones. Se basa en el proyecto Fedora de código abierto, pero está diseñado para ser una plataforma estable con soporte a largo plazo.

Red Hat utiliza la ley de marcas registradas para evitar que el software de Red Hat Enterprise Linux se redistribuya. Sin embargo, el software central es gratuito y de código abierto.

arco linux

Arch es posiblemente la distribución de Linux más dura. Es muy ligero, flexible y minimalista. Con Arch, el usuario está completamente a cargo de configurar el sistema. El propósito de Arch no es ser mainstream. Está destinado a usuarios que tienen un conocimiento profundo de cómo funcionan una computadora y un sistema operativo, o que al menos están interesados ​​en aprender.

Puede obtener más información sobre Arch y cuánto puede personalizarlo en este manual detallado .

Aquí hay un gran video que resume rápidamente la historia de GNU/Linux y repasa las características de las principales distribuciones. Fireship es otro canal increíble que recomiendo. ;)

En cuanto al modelo de negocio de GNU/Linux, bueno, no es un negocio para empezar. Tanto Linux como la Free Software Foundation (la organización detrás de GNU) son ONG que funcionan gracias a las donaciones.

Linux, por ejemplo, gana dinero a través de membresías Platinum, Gold, Silver e Individual.

Empresas como Microsoft, Google, Facebook, Cisco, Fujitsu, HPE, Huawei, IBM, Intel, Oracle, Qualcomm y Samsung contribuyen activamente a la fundación Linux. Esto tiene sentido para las empresas porque todas se benefician del conocimiento y la tecnología generados por Linux, y sus donaciones también pueden ser deducibles de impuestos.

Con respecto a las distribuciones, algunas de ellas son completamente gratuitas y mantenidas por voluntarios y otras son mantenidas por empresas y son gratuitas para usuarios particulares pero comercializadas para usuarios corporativos. Otro modelo de negocio utilizado es el uso gratuito pero cobrando por soporte a usuarios corporativos.

Hoy, Linux se ejecuta en la mayoría de los servidores en todo el mundo. Se usa en la mayoría de las supercomputadoras y también en la mayoría de los teléfonos celulares (como se mencionó anteriormente, Android usa el kernel de Linux).

En el lado de las computadoras de escritorio/portátiles, el uso de Linux no está tan extendido. Y eso probablemente se deba a que no está tan disponible de forma predeterminada como Windows, y no está tan comercializado como Mac.

Además, especialmente en el pasado, la curva de aprendizaje necesaria para implementar y usar Linux era considerablemente más alta que para las otras dos opciones de sistema operativo.

De todos modos, esta situación ha estado cambiando últimamente a medida que las distribuciones de Linux se enfocan más en la amabilidad del usuario y es más fácil que nunca tener computadoras con distribuciones de Linux instaladas de manera predeterminada.

Windows vs Mac vs Linux - Comparación de sistemas operativos

Bien, además de la historia, el modelo comercial, etc., ¿cuáles son las diferencias reales para el usuario cuando se trata de estos tres sistemas operativos?

La respuesta corta no es tanto, en realidad. Pero repasemos algunas diferencias en el diseño, las características y la experiencia de usuario de estos sistemas operativos, y más adelante les daré mi opinión al respecto.

Sistemas de archivos

La forma en que Windows organiza los archivos es diferente a la forma en que lo hacen Mac y GNU/Linux.

Windows usa "unidades". Por lo general, hay una unidad C y D que almacena todos los archivos de la computadora y unidades separadas para dispositivos externos como CD, USB, etc.

99219e7-6b1f-4528-93d5-495994b77a5e

Mac y GNU/Linux tienen un sistema de archivos similar que proviene de UNIX. En estos sistemas operativos no hay unidades: todo en la computadora se considera un archivo (incluso los dispositivos externos) y todos los archivos están organizados en directorios que descienden de un solo directorio raíz. La estructura de directorios se forma como un árbol que tiene una raíz única.

Esto no necesariamente hace una gran diferencia para el usuario final, pero es algo a tener en cuenta si está acostumbrado a navegar por un tipo de sistema de archivos u otro.

conchas

Tanto GNU/Linux como Mac tienen Bash como shell predeterminado, mientras que Windows tiene su propio shell que usa una sintaxis diferente.

Como desarrolladores y ávidos usuarios de terminales, aprender Bash es probablemente la mejor opción, ya que este conocimiento se puede traducir más fácilmente a todos los sistemas operativos que al shell de Windows. Sobre todo teniendo en cuenta que GNU/Linux se ejecuta en la mayoría de los servidores de todo el mundo, que es una de las principales ocasiones en las que necesitarías usar la terminal para interactuar con la computadora.

Si desea obtener más información sobre los shells y el uso de terminales, recientemente escribí un artículo al respecto .

Administradores de paquetes

Mac y GNU/Linux vienen con administradores de paquetes instalados de forma predeterminada. Un administrador de paquetes es una pieza de software que le permite instalar, actualizar y desinstalar programas desde la terminal, simplemente ingresando algunos comandos.

Son muy útiles, especialmente cuando instalas y desinstalas cosas constantemente, ya que es mucho más eficiente instalar programas a través de administradores de paquetes que manualmente.

El administrador de paquetes de Mac se llama homebrew . En GNU/Linux, el administrador de paquetes predeterminado depende de la distribución. Por ejemplo, Ubunto viene con APT , Arch viene con Pacman , etc.

Todos los administradores de paquetes funcionan de manera similar, pero existen algunas diferencias en la sintaxis utilizada para cada uno. También es importante mencionar que puede instalar y ejecutar un administrador de paquetes diferente al predeterminado.

Windows no viene con un administrador de paquetes predeterminado. Si desea uno, debe instalarlo primero. Uno de los administradores de paquetes disponibles para Windows es Chocolatey .

Costo

Como ya se mencionó, la mayoría de las distribuciones de GNU/Linux son completamente gratuitas para que cualquiera las use. Windows tiene un modelo freemium actualmente y MacOS solo se ejecuta en computadoras Mac, que son bastante caras, como sabrá.

Compatibilidad de programas

Windows es el sistema operativo más utilizado y, gracias a ello, la mayoría de los programas se adaptan a él. Aunque menos popular, MacOS es similar a Windows en este sentido.

En el pasado, Linux no era compatible con muchos programas, pero esto ha comenzado a cambiar recientemente, especialmente con las distribuciones más populares como Ubuntu.

Calidad y compatibilidad del hardware

Cuando se trata de hardware, solo Apple tiene la responsabilidad directa de las computadoras en las que se ejecuta el sistema operativo. Y el hardware de Apple es uno de los mejores que existen.

Como empresa, Apple se enfoca en brindar productos de la mejor calidad, por lo que sus computadoras más nuevas tienden a ser las que tienen el mejor rendimiento en todo el mercado.

Dado que Apple diseña y desarrolla tanto el hardware como el software, es posible que la compatibilidad entre la máquina y el sistema operativo sea más precisa que con Windows o GNU/Linux.

En el lado de Windows y GNU/Linux, la calidad del hardware depende completamente de lo que el usuario decida o pueda permitirse comprar. Lo bueno aquí es que puedes instalar el sistema operativo donde quieras.

Esto es particularmente bueno cuando se piensa en instalar distribuciones de Linux livianas en computadoras más antiguas que no pueden manejar los requisitos de sistemas operativos más grandes y que consumen más como Windows.

Facilidad de uso

Windows y Mac son sistemas operativos realmente simples y fáciles de usar. En cuanto a GNU/Linux, depende de la distribución que elijas. Como se mencionó, las distribuciones como Ubuntu son prácticamente tan fáciles como Windows o Mac, y otras como Arch están destinadas a usuarios avanzados de computadoras.

Seguridad y estabilidad

Algunas distribuciones de GNU/Linux se consideran las más seguras y estables en la actualidad. El hecho de que el código esté disponible para todos no es una amenaza para la seguridad, como puede pensar al principio, sino que es una ventaja. Los errores se pueden identificar y solucionar más rápido, y cuando se identifica una brecha de seguridad, mucha gente puede trabajar en ella y proponer soluciones.

Windows, por otro lado, se considera el menos seguro y estable de los tres. Dado que es el sistema operativo más popular, la mayoría del malware también se desarrolla para atacar el sistema operativo Windows.

Comunidad y cultura

Si está interesado en aprender más sobre un sistema operativo en particular, estudiar cómo funciona, cómo modificarlo y crear proyectos basados ​​en él, GNU/Linux es definitivamente el camino a seguir. Es el único que tiene su código disponible para cualquiera y su comunidad online es enorme.

Aunque GNU/Linux no se usa tanto como los otros dos sistemas operativos, encuentro que los usuarios de Linux suelen ser personas interesadas en el software y la tecnología, y personas a las que les gusta hablar, aprender y compartir conocimientos al respecto.

Mac también tiene su grupo de fanáticos y es particularmente popular entre los creativos (diseñadores gráficos, editores de video, animadores, etc.).

Y, finalmente, Windows es comúnmente utilizado por el usuario general y en entornos corporativos.

En cuanto a la cultura organizacional, creo que podría ser interesante visualizarla en el ambiente laboral de las personas que crearon estos SOs:

Si desea ver una comparación más detallada de estos tres sistemas operativos, Zach Gollwitzer tiene un video muy bueno sobre este tema (otro gran canal para seguir ;)).

Qué sistema operativo elegir

Recientemente tuve la oportunidad de usar los tres sistemas operativos y, como mencioné, no creo que las diferencias entre cada uno de ellos sean TAN grandes.

En mi opinión, Linux es una opción inteligente porque funciona muy bien, se usa ampliamente en la industria tecnológica (por lo que todo el conocimiento se puede traducir a entornos de trabajo) y, si está interesado en aprender más sobre cómo funciona, hay una gran comunidad. que apoya eso. Y lo más importante de todo... ¡es gratis!

Quiero decir, si tenemos una de las mejores y más utilizadas piezas de software en la historia de la humanidad a nuestro alcance y completamente gratis, ¿por qué pagaríamos para obtener algo más?

En cuanto a otros asuntos, creo que la mayoría de las cosas que puedes hacer en GNU/Linux también puedes hacerlas en Mac y Windows, al menos para la mayoría de los usuarios. Probablemente no hará una gran diferencia en tu vida diaria, al menos desde mi perspectiva.

En cuanto al hardware, comprar una computadora Apple moderna es casi una garantía de tener una máquina de gran rendimiento (si puede pagarla). Pero si sabe un poco sobre hardware o se toma el tiempo de investigar, también puede encontrar fácilmente muy buenas opciones por un precio más bajo.

Al final, creo que es importante saber lo que estás usando y conocer las opciones que hay. Como usuarios de computadoras, es una buena idea estar al tanto de los hechos y las diferencias, y evitar distraerse con las campañas de marketing.

Tampoco creo en poner demasiado juicio o peso en una elección u otra. El hecho de que alguien elija un sistema operativo de código abierto no hace que esa persona sea más inteligente o superior que alguien que no lo hace... Al igual que tener la última computadora Mac no te hará un mejor programador.

Para resumir, lo que elijas está bien siempre que tu sistema te permita hacer lo que quieras.

Como siempre, espero que hayas disfrutado el artículo y hayas aprendido algo nuevo. 

¡Salud y nos vemos en la próxima! =D

Fuente: https://www.freecodecamp.org/news/an-introduction-to-operating-systems/

#operator #system #linux #mac #windows 

Windows Vs MacOS Vs Linux – Manual Del Sistema Operativo
藤本  結衣

藤本 結衣

1649858820

Windows vs MacOS vs Linux –オペレーティングシステムハンドブック

やあみんな!

この記事では、オペレーティングシステムについて簡単に紹介し、現在存在する3つの主要なOSを比較します。

まず、OSとは何か、そしてOSについての歴史はほとんどありません。次に、最も一般的なオペレーティングシステム(Windows、Mac、GNU / Linux)の主な機能と違いを確認します。

ここでのアイデアは、彼らの歴史、彼らがどのようにそして誰によって開発されたか、彼らのビジネスモデル、そして彼らの賛否両論を探求することです。これにより、それらがどのように機能し、どれを選択するかについて、より良いアイデアが得られます。

私はこの主題についての事実と私の個人的な意見を共有するつもりです。したがって、ここで言及することのいくつかは、私自身の経験とトピックの分析に基づいていることを覚えておいてください。

また、特定のテーマに飛び込みたい場合に備えて、ご覧いただける追加の記事やビデオも多数提供します。

それ以上の苦労なしに、行きましょう!

オペレーティングシステムとは何ですか?

ウィキペディアによると、

「オペレーティングシステム(OS)は、コンピューターのハードウェア、ソフトウェアリソースを管理し、コンピュータープログラムに共通のサービスを提供するソフトウェアシステムです」。

OSは、コンピューターとその上で実行する他のすべてのプログラムの間にある「中間」プログラムと考えることができます。ファイル管理、メモリ管理、プロセス管理、入出力管理、周辺機器の制御などの重要な基本タスクを管理します。

OSは、コンピューターの使用を簡素化するために作成されました。現在、特定のプログラムは、そのコア機能の実行についてのみ心配し、すべての基本的なシステム機能をOSに任せることができます。しかし、物事は必ずしもこのようではありませんでした...

オペレーティングシステムの歴史

an9zgv0_700b

昔(1940年代から50年代)には、プログラムは特定のマシンで実行するように作成されていました。つまり、プログラムは1つのコンピューターモデルでのみ実行できます。

同じプログラムを別のコンピューターモデルで実行したい場合、ハードウェアが別の方法で構成されているため、プログラマーはプログラム全体を再度作成する必要があります。実行中のプログラムと実際のハードウェアの間に抽象化レイヤーはありませんでした。

サイドコメント:当時、プログラマーの仕事を止めて考えたことはありますか?プログラムはパンチカードで書かれました!= O
考えるたびに頭がおかしくなります...当時のレベルがいかに低かったかは驚くべきことであり、初期のプログラマーのおかげで進歩技術が達成されました。

837755a86e841b0452e12f0786128e02

1960年代までに、IBMやAT&Tなどの業界の巨人は、ハードウェアとソフトウェアの間の抽象化レイヤーとして機能し、新しいプログラムの実装を簡素化できるオペレーティングシステムの開発に着手しました。

これらのプロジェクトで最も悪名高いのはUnixで、これはAT&Tのベル研究所で開発者のKen Thompson(現在Goプログラミング言語の開発に取り組んでいる)とDennis Ritchie(Cプログラミング言語も作成した)によって開発されたOSでした。伝説、うん。)。

ケン・トンプソン-デニス・リッチー-111013

Unixは大成功を収め、非常によく似た特性を持つ他の多くのOSの作成に影響を与えました。これらは後でGNU/LinuxとMacOに大きな影響を与えましたが、これについては後で確認します。

1980年代までに、コンピュータのパフォーマンス、アクセシビリティ、サイズ、および価格は、一般の人々がそれらを購入して個人的なタスクに使用できるようになるまで改善されました。これにより、OSは企業固有の機能から一般的な使用法に移行しました。そして、これは私たちを現代に連れて行きます...

OSの動作とその歴史についてのより詳細な説明に興味がある場合は、こちらのすばらしいビデオをご覧ください。このチャンネルには、コンピュータサイエンスに関する信じられないほどのクラッシュコースシリーズもあります。私は間違いなくそれをお勧めします!;)

3つの主要なOS

現代では、パーソナルデスクトップ/ラップトップコンピューターについて話すとき、最も使用されている3つのオペレーティングシステムは、Microsoft Windows(約80%の市場シェア)、Apple MacO(約15%の市場シェア)、およびGNU / LinuxベースのOS(約15%の市場シェア)です。約3%の市場シェア)。

サーバーに関しては、約80%がGNU / Linuxを実行し、20%がWindowsを実行しています。また、モバイルデバイスについて言えば、約75%がAndroid(Linuxカーネルを使用)を実行し、25%がIO(AppleのモバイルOS)を実行しています。

それぞれを個別に簡単に確認し、後ですべてを比較して違いを特定します。

Windowsオペレーティングシステム

516094c7ed4e0--1-

Windowsの祖先は、1981年にMicrosoftが実現したテキストベースのOSであるMS-DOSです。

MS-DOSはIBMPCと互換性があるように開発され、非常に成功しました。しかし、一般の人々がアクセスしやすくするためには、GUIが必要でした。これは、Microsoftが1985年にWindows1.0とともに出荷したものです。

それ以来、Windowsは95、98、XP、Vistaなどの多くのバージョンをリリースしました...そしてそれ自体が世界中で最も広く使用されているオペレーティングシステムになりました。

Windowsのアクセシビリティと、ほとんどのパーソナルコンピュータにプリインストールされているという事実(商用契約のおかげで)により、このOSは今日まで最も人気のあるOSになっています。

これは、Windowsの歴史をわずか3分で要約したクールなビデオです。

また、Microsoftの歴史について詳しく知りたい場合は、別のすばらしいビデオをご覧ください。

そのビジネスモデルに関して、私はWindowsの戦略は市場を氾濫させ、そのシステムを可能な限りアクセス可能で使いやすいものにすることだと思います。彼らの主なターゲット顧客は一般ユーザーであるため、カスタマイズ、セキュリティ、またはパフォーマンスはそれほど重要視されていません。

ほとんどの人にとって、Windowsは単なるデフォルトのOSです。これは彼らが知る最初のものであり、ユーザーは多くの設定をまったく行わなくても、日常のタスク(インターネットブラウジング、ゲーム、事務作業)を簡単に実行できます。

Windowsはプライベートなソフトウェアであり、そのソースコードは公開されていません。Microsoftだけがそれにアクセスできます。

最初は、ユーザーがWindows OSのコピーを購入したり、Windowsバージョンをアップグレードしたりする場合は、料金を支払う必要がありました。しかし、最新のリリースでは、Windowsはフリーミアムモデルを採用しています。このビジネスモデルでは、ユーザーはほとんどのソフトウェア機能に無料でアクセスでき、特定の機能にアクセスするには料金を支払うだけで済みます。

この変化を理解するための鍵は、Microsoftが非常に多様なビジネスのポロフォリオを持っていることを理解することです(Xbox-ゲーム、Azzure-クラウドプラットフォーム、Linkedin-ソーシャルネットワーク、Bing-検索エンジン、Github...少し)。Windowsを無料にすることで、市場は氾濫し続け、人々がWindowsをデフォルトのOSとして採用するのがさらに容易になります。

覚えておくべきもう一つのことは、Windowsがオペレーティングシステム内に広告を表示することです。したがって、広告プラットフォームとしても考えることができます。

この動きを説明するさらに別のクールなビデオがここにあります。

そして、マイクロソフトの古い学校のマーケティングスタイルの奇妙な/面白い/小さなビット怖い例。

マックOS

How-do-I-plug-a-USB-stick-into-this--2000-Macbook-meme-7242

MacOS(以前はOS Xと呼ばれていました)は、Appleによって作成された一連のオペレーティングシステムです。すべてのMacintoshコンピュータまたはMacにプリインストールされています。その最初のバージョンは1984年にリリースされ、GUIが組み込まれたパーソナルコンピュータ用の最初のOSでした。

MacOSはUNIXライクなOSの上に構築されているため、このMacOSはGNU/Linuxから派生したものと多くの共通の特徴を共有しています。

私の意見では、Appleのビジネスモデルは主に差別化と独占性に基づいています。Microsoftとは異なり、Appleは自社製品のハードウェアとソフトウェアの両方を製造しており、Appleのソフトウェアは自社のマシンでのみ実行されます。

アップルは、テクノロジー市場でトップクラスのメーカーとしての地位を確立しており、競合他社のほとんどよりもかなり高い価格で、高品質のハードウェアとソフトウェアを顧客に提供することを目指しています。

独占権はユーザーへの特典としても促進され、アップル製品を所有するときに選ばれた人々のグループの一員であるという考えを売ります。

ハードウェアで必要なソフトウェアを実行できないこと、およびMacマシン以外の場所にソフトウェアをインストールできないことも同じ考えの一部です。グループの一員になりたい場合は、パッケージ全体を購入する必要があります。

Appleは、ソフトウェアとハ​​ードウェアのほとんどを異なる方法で作成し、多くの場合、他のソフトウェアと互換性がありません。製品をできるだけ広く入手可能で入手しやすいものにすることを目的とするMicrosoftとは異なり、Appleは製品を最高品質でありながら高価であり、他のハードウェアと互換性がないようにすることを目指しています。

Appleによるもう1つの優れたマーケティングの動きは、 SteveJobsのような人々の非常にカリスマ的で影響力のある人格から利益を得る能力です。彼らは、業界のリーダー、イノベーター、そしてどういうわけか「反逆者」としての彼の立場と軌跡を利用して、同じ価値観を暗黙のうちに製品に変換しました。

私が何を意味するかを知るためにこれらの広告を見てください:

MacOSの歴史についてもっと知りたい場合は、こちらのビデオをご覧ください。

GNU / Linux

GNU / Linuxは、多くのオープンソースOSのベースです。今見た例とは異なり、GNU / Linuxは完全なオペレーティングシステムではありませんが、多くのオープンソースOSが共有する一連のプログラム/ユーティリティとカーネルです。

各部分を個別に確認してみましょう。

GNUは、 RichardStallmanによって始められたプログラムとユーティリティの膨大なコレクションです。

EinHz4EWkAQYthk

GNUプロジェクトは、無料のUNIXライクなOSを開発するというアイデアで1983年に開始されました(UNIXはAT&Tの所有物であったため、無料では利用できませんでした)。ストールマンはOSに必要なプログラムとユーティリティの開発を開始しましたが、重要な部分が1つ欠けていました。それはカーネルです。

カーネルはあらゆるOSの心臓部です。これは、ハードウェアと最も密接に相互作用するソフトウェアであり、OSの残りの部分はその上にあります。カーネルは、ディスク管理、メモリ管理、タスク管理などの低レベルのタスクを担当します。

1991年までに、ヘルシンキ大学のLinus Torvaldsという学生が、UNIXライクなOS用のカーネルの開発を開始しました。

this-finger-wrote-linux

次の年に、両方のプロジェクトが相互作用し始め、すべてのOSが使用できる強固な基盤を形成するために一緒に結合されました。

ここで重要なのは、両方のプロジェクトがオープンソースであり、完全にフリーソフトウェアであるということです。これの意味は:

  • 目的を問わず、誰でも自由にプログラムを実行できます。
  • 誰でも自由にプログラムがどのように機能するかを研究し、プログラムを変更して自分の望むことを実行できるようにします。
  • 元のソフトウェアのコピーは誰でも自由に再配布できます。
  • ソフトウェアの変更されたバージョンのコピーは、誰でも自由に配布できます。

自由ソフトウェア運動をよりよく理解するために、リチャードによるこのTEDトークを聞いてください

そして、リチャードがスペイン語を話し、自由ソフトウェアについての歌を歌うのを見てください(あなたはこの男を愛さなければなりません...)。

ストールマンとトーバルズがGNU/Linuxの開発で採用したアプローチは、これまでに見た例や、それまで業界が使用していたものとは根本的に異なります。

GNU / Linuxを無料にすることは、開発者の観点からは正しいことであるだけでなく、ソフトウェア品質の観点からも優れた選択でした。これは、世界中の何千もの開発者や企業がシステムを改善するために無料で協力することを選択しているためです。

GNU / Linuxディストリビューションのいくつかは、世の中で最も安全で安定したOSであることが知られています。これらは、銀行、金融、政府、軍隊などの主要な分野で使用されています。

これの大部分は、GNU / Linuxの背後にあるオープンソースモデルのおかげであり、世界中の何千人もの人々がコードをレビューし、バグを修正し、改善を絶えず提案することができます。

Linux Foundationによるこれらの2つのビデオでは、Linuxがどのように生まれ現在どのように動作しているかを説明しています。

前述のように、GNU/Linuxは他の多くのOSのベースとして機能します。これらのOSは、Linuxの世界では「ディストリビューション」または「ディストリビューション」と呼ばれています。すべての共通点は、同じカーネルと一連のユーティリティに基づいているということです。それらはLinuxの「フレーバー」と考えることができます。

特定のディストリビューション間で大きな違いはありませんが、他のディストリビューションには言及する価値のある違いがあります。これをよりよく理解するために、最もよく使用されるディストリビューションをすばやく確認しましょう。

Debian

Debianは、無料のオープンソースソフトウェアのみを含むOSです。Debianは1993年に開始され、現在も強力で新しいバージョンをリリースしています。Debianは主にその安定性とセキュリティで知られており、新しいリリースに関してはより保守的で「遅い」ものになっています。

Ubuntu

Ubuntuは最も広く使用されているGNU/Linuxディストリビューションです。これは、Debianのコア部分を取り入れ、それらをより迅速に改善するために作成されました。また、使いやすさとアクセシビリティに重点を置いているため、WindowsまたはMacOSのバックグラウンドを持っている人にとってはおそらく最良の選択肢になります。

Ubuntuは通常、6か月ごとにリリースを提供し、2年ごとにより安定したLTS(長期サポート)リリースを提供します。UbuntuはCanonicalという会社によって運営されています。

ミント

Mintは、Ubuntu上に構築されたディストリビューションです。もともとは、Ubuntuには含まれていなかったメディアコーデックとプロプライエタリソフトウェアが含まれていたため、多くの人に愛されていました。

Fedora

Fedoraは、自由ソフトウェアに重点を置いたディストリビューションです。Fedoraは、同時にIBMが所有するRedHatという会社によって後援されています。

Red Hat Enterprise Linux

Red Hat Enterprise Linuxは、NasdaqにリストされているRedHatという会社によって管理されている商用Linuxディストリビューションです。OSは主にサーバーや企業で使用されています。これはオープンソースのFedoraプロジェクトに基づいていますが、長期的なサポートを備えた安定したプラットフォームになるように設計されています。

Red Hatは商標法を使用して、Red HatEnterpriseLinuxソフトウェアが再配布されるのを防ぎます。ただし、コアソフトウェアは無料でオープンソースです。

Arch Linux

Archはおそらく最もハードコアなLinuxディストリビューションです。非常に軽量で柔軟性があり、最小限です。Archを使用すると、ユーザーはシステムの構成を完全に担当します。Archの目的は主流になることではありません。これは、コンピューターとOSがどのように機能するかを深く理解しているユーザー、または少なくとも学習に興味があるユーザーを対象としています。

Archの詳細と、Archをカスタマイズできる量については、この詳細なハンドブックを参照してください。

これは、GNU / Linuxの歴史をすばやく要約し、主要なディストリビューションの特徴を説明するすばらしいビデオです。火船は私がお勧めするもう1つの素晴らしいチャンネルです。;)

GNU / Linuxのビジネスモデルに関しては、最初から始めるのに忙しいわけではありません。Linuxとフリーソフトウェアファウンデーション(GNUの背後にある組織)はどちらも寄付のおかげで運営されているNGOです。

たとえば、Linuxは、プラチナ、ゴールド、シルバー、および個人のメンバーシップを通じて収益を上げています。

Microsoft、Google、Facebook、Cisco、Fujitsu、HPE、Huawei、IBM、Intel、Oracle、Qualcomm、Samsungなどの企業はすべて、LinuxFoundationに積極的に貢献しています。これは企業にとって理にかなっています。なぜなら、すべての企業がLinuxによって生成された知識とテクノロジーの恩恵を受けており、寄付も税控除の対象となる可能性があるからです。

ディストリビューションに関しては、完全に無料でボランティアによって保守されているものもあれば、企業によって保守されており、特定のユーザーには無料ですが、企業ユーザーには商用化されているものもあります。使用されるもう1つのビジネスモデルは、無料で使用できますが、企業ユーザーのサポートには料金がかかります。

現在、Linuxは世界中のほとんどのサーバーで実行されています。ほとんどのスーパーコンピューターとほとんどの携帯電話で使用されています(前述のように、AndroidはLinuxカーネルを使用します)。

デスクトップ/ラップトップの面では、Linuxの使用はそれほど普及していません。これはおそらく、デフォルトではWindowsほど広く利用可能ではなく、Macほど市場に出回っていないためです。

また、特に当時は、Linuxの実装と使用に必要な学習曲線は、他の2つのOSオプションよりもかなり高かった。

とにかく、この状況は最近変化しており、Linuxディストリビューションはユーザーフレンドリーに重点を置いており、Linuxディストリビューションがデフォルトでインストールされているコンピューターをこれまで以上に簡単に入手できます。

Windows対Mac対Linux-OSの比較

さて、歴史やビジネスモデルなどの他に、これら3つのオペレーティングシステムに関して、ユーザーにとって実際の違いは何ですか?

簡単な答えは、実際にはそれほど多くはありません。ただし、これらのオペレーティングシステムの設計、機能、およびユーザーエクスペリエンスの違いを確認してみましょう。後で、これについて意見を述べます。

ファイルシステム

Windowsがファイルを整理する方法は、MacやGNU/Linuxが行う方法とは異なります。

Windowsは「ドライブ」を使用します。通常、すべてのコンピューターファイルを格納するCドライブとDドライブがあり、CDやUSBなどの外部デバイス用に別々のドライブがあります。

99219e7-6b1f-4528-93d5-495994b77a5e

MacとGNU/Linuxには、UNIXに由来する同様のファイルシステムがあります。これらのOSにはドライブがありません。コンピューター内のすべてがファイルと見なされ(外部デバイスも含む)、すべてのファイルは単一のルートディレクトリから派生したディレクトリに編成されます。ディレクトリ構造は、一意のルートを持つツリーとして形成されます。

これは必ずしもエンドユーザーにとって大きな違いにはなりませんが、あるタイプのファイルシステムまたは他のタイプのファイルシステムをナビゲートすることに慣れている場合は注意が必要です。

シェル

GNU / LinuxとMacの両方にデフォルトのシェルとしてBashがありますが、Windowsには異なる構文を使用する独自のシェルがあります。

開発者および熱心な端末ユーザーとして、この知識はWindowsシェルよりもすべてのOSに簡単に変換できるため、Bashを学ぶことがおそらく最良の選択です。特に、GNU / Linuxが世界中のほとんどのサーバーで実行されていることを考慮すると、これは、コンピューターと対話するために端末を使用する必要がある主な機会の1つです。

シェルと端末の使用法についてもっと知りたい場合は、最近それについての記事を書きました。

パッケージマネージャー

MacとGNU/Linuxには、デフォルトでパッケージマネージャーがインストールされています。パッケージマネージャーは、いくつかのコマンドを入力するだけで、ターミナルからプログラムをインストール、更新、およびアンインストールできるソフトウェアです。

手動よりもパッケージマネージャーを介してプログラムをインストールする方がはるかに効率的であるため、特に常にインストールとアンインストールを行う場合に非常に役立ちます。

Macのパッケージマネージャーは自作と呼ばれています。GNU / Linuxでは、デフォルトのパッケージマネージャーはディストリビューションに依存します。たとえば、UbuntoにはAPTが付属し、ArchにはPacmanが付属しています。

すべてのパッケージマネージャーは同じように機能しますが、それぞれに使用される構文にいくつかの違いがあります。デフォルトとは異なるパッケージマネージャーをインストールして実行できることにも言及することが重要です。

Windowsにはデフォルトのパッケージマネージャーが付属していません。必要な場合は、最初にインストールする必要があります。Windowsで利用できるパッケージマネージャーの1つはChocolateyです。

費用

すでに述べたように、ほとんどのGNU / Linuxディストリビューションは、誰でも完全に無料で使用できます。Windowsには現在フリーミアムモデルがあり、MacOSはMacコンピュータでのみ動作します。これはご存知かもしれませんがかなり高価です。

ソフトウェアの互換性

Windowsは最も広く使用されているOSであり、そのおかげでほとんどのソフトウェアがそれに適合しています。あまり人気がありませんが、MacOSはこの点でWindowsに似ています。

当時、Linuxはそこにある多くのプログラムと互換性がありませんでしたが、これは最近、特にUbuntuのような最も人気のあるディストリビューションで変化し始めています。

ハードウェアの品質と互換性

ハードウェアに関しては、OSが実行されているコンピュータに対して直接責任を負うのはAppleだけです。そして、Appleのハードウェアはそこにある最高のもののいくつかです。

会社として、アップルは最高品質の製品を提供することに焦点を合わせているので、彼らの最新のコンピュータは、市場全体で最高のパフォーマンスを備えたものになる傾向があります。

Appleがハードウェアとソフトウェアの両方を設計および開発していることを考えると、マシンとOSの間の互換性は、WindowsまたはGNU/Linuxよりも細かく調整されている可能性があります。

WindowsおよびGNU/Linux側では、ハードウェアの品質は、ユーザーが決定するか、購入する余裕があるかどうかに完全に依存します。ここでの良い点は、OSを好きな場所にインストールできることです。

これは、Windowsのような大きくて消費量の多いOSの要件を処理できない古いコンピューターに軽量Linuxディストリビューションをインストールすることを考えると特にクールです。

使いやすさ

WindowsとMacは本当にシンプルでユーザーフレンドリーなOSです。GNU / Linuxに関しては、選択したディストリビューションによって異なります。前述のように、UbuntuのようなディストリビューションはWindowsやMacと同じくらい簡単で、Archのような他のディストリビューションは上級コンピューターユーザーを対象としています。

セキュリティと安定性

一部のGNU/Linuxディストリビューションは、現在最も安全で安定しているディストリビューションと見なされています。コードがすべての人に利用可能であるという事実は、最初に考えるかもしれないセキュリティの脅威ではありませんが、むしろそれは利点です。バグを特定してより迅速に対処でき、セキュリティ違反が特定されると、多くの人がバグに取り組み、修正を提案できます。

一方、Windowsは、3つの中で最も安全性が低く安定していると見なされています。最も人気のあるOSであることを考えると、ほとんどのマルウェアはWindowsOSを攻撃するためにも開発されています。

コミュニティと文化

特定のOSについて詳しく知り、その動作、変更方法、およびそれに基づいたプロジェクトの作成方法を研究することに興味がある場合は、GNU/Linuxが最適です。コードを誰でも利用できるのはこれだけであり、そのオンラインコミュニティは巨大です。

GNU / Linuxは他の2つのOSほど広く使用されていませんが、Linuxユーザーは通常、ソフトウェアとテクノロジーに興味を持っている人々であり、それについて話し、学び、知識を共有するのが好きな人々です。

Macにもファンがいて、クリエイティブ(グラフィックデザイナー、ビデオエディター、アニメーターなど)の間で特に人気があります。

そして最後に、Windowsは一般ユーザーや企業環境で一般的に使用されています。

組織文化に関しては、このOSを作成した人々の作業環境でそれを視覚化することは興味深いかもしれないと思います。

これらの3つのOSをより詳細に比較したい場合は、Zach Gollwitzerがこのトピックに関する非常に優れたビデオを公開しています(フォローするもう1つの優れたチャネル;))。

選択するオペレーティングシステム

最近、3つすべてのOSを使用する機会がありましたが、前述したように、それぞれのOSの違いはそれほど大きくないと思います。

私の意見では、Linuxはうまく機能し、テクノロジー業界全体で広く使用されているため(すべての知識を作業環境に変換できるため)、賢明な選択です。Linuxの機能について詳しく知りたい場合は、巨大なコミュニティがあります。それをサポートします。そして何よりも重要なのは...無料です!

つまり、人類の歴史の中で最も広く使用されているソフトウェアの1つが手の届く範囲にあり、完全に無料であるとしたら、なぜ他のものを手に入れるためにお金を払うのでしょうか。

その他の問題に関しては、GNU / Linuxでできることのほとんどは、少なくともほとんどのユーザーにとって、MacとWindowsでもできると思います。少なくとも私の観点からは、それはおそらくあなたの日常生活に大きな違いをもたらすことはないでしょう。

ハードウェアに関しては、最新のAppleコンピュータを購入することは、(余裕があれば)優れたパフォーマンスのマシンを持っていることをほぼ保証します。しかし、ハードウェアについて少し知っているか、時間をかけて調査する場合は、低価格で非常に優れた選択肢を簡単に見つけることができます。

最後に、何を使用しているかを知り、そこにあるオプションを知ることが重要だと思います。コンピュータユーザーとして、事実と違いを認識し、マーケティングキャンペーンに気を取られないようにすることをお勧めします。

私はまた、どちらか一方の選択に過度の判断や重みを置くことを信じていません。誰かがオープンソースOSを選んだという事実は、その人をそうでない人よりも賢くも優れたものにもしません...最新のMacコンピューターを所有しているからといって、あなたが優れたプログラマーになるわけではありません。

簡単に言えば、システムがあなたが望むことをすることができる限り、あなたが選んだものは何でも構いません。

いつものように、あなたが記事を楽しんで、何か新しいことを学んだことを願っています。 

乾杯、次でお会いしましょう!= D

出典:https ://www.freecodecamp.org/news/an-introduction-to-operating-systems/

#operator #system #linux #mac #windows 

Windows vs MacOS vs Linux –オペレーティングシステムハンドブック
Bulah  Pfeffer

Bulah Pfeffer

1648873833

A Collection of Flutter and Dart Tips and Tricks

Table of Contents

  • LazyStream in Flutter and Dart
  • Cancelable APIs in Flutter
  • Asset Data in Flutter
  • API Caching in Flutter
  • FutureGroup in Dart
  • Flatten Iterable<bool> in Dart
  • Caching Temp Files in Flutter
  • Custom Lists in Dart
  • Optional Chaining in Dart
  • MapList in Flutter
  • Future<bool> in Flutter
  • Async Bloc Init in Flutter
  • Firebase Auth Errors in Flutter
  • Debug Strings in Flutter
  • Keyboard Appearance in Flutter
  • Get String Data in Dart
  • Stream.startWith in Flutter
  • Optional Functions in Dart
  • AnnotatedRegion in Flutter
  • Unordered Map Equality in Dart
  • Iterable to ListView in Flutter
  • Password Mask in Flutter
  • Fast Object.toString() in Dart
  • Copying Bloc State in Flutter
  • Iterable Subscripts in Dart
  • useState in Flutter Hooks
  • Folding Iterables in Dart
  • Custom Iterables in Dart
  • Class Clusters in Dart
  • Iterable +/- in Dart
  • Periodic Streams in Dart
  • EmptyOnError in Dart
  • Stream<T> Initial Value in Flutter
  • Double.normalize in Dart
  • Hide Sensitive Information in Flutter
  • Iterable.compactMap in Dart
  • useEffect in Flutter Hooks
  • Merging Streams in Dart
  • Isolate Stream in Dart
  • Network Image Retry in Flutter
  • Reusable APIs in Flutter
  • ListTile Shadow in Flutter
  • Transparent AppBar in Flutter
  • Constructors on Abstract Classes in Dart
  • @useResult in Dart
  • @mustCallSuper in Dart
  • Object.hash in Dart
  • Expanded Equally in Flutter
  • Random Iterable Value in Dart
  • Hardcoded Strings in Flutter
  • Manually Scroll in List View in Flutter
  • AsyncSnapshot to Widget in Flutter
  • Breadcrumbs in Flutter
  • Unique Map Values in Dart
  • Smart Quotes/Dashes in Flutter
  • Haptic Feedback in Flutter
  • Localization Delegates in Flutter
  • Extending Functions in Dart
  • Paginated ListView in Flutter
  • Immutable Classes in Dart
  • Card Widget in Flutter
  • List Equality Ignoring Ordering in Dart
  • Shorten GitHub URLs in Dart
  • Time Picker in Flutter
  • Throttled Print in Flutter
  • Map Equality in Dart
  • Unique Maps in Dart
  • Raw Auto Complete in Flutter
  • Title on Object in Dart
  • Compute in Flutter
  • Filter on Map in Dart
  • Type Alias in Dart
  • ValueNotifier in Flutter
  • Object to Integer in Dart
  • Image Opacity in Flutter
  • Covariant in Dart
  • Custom Errors in Streams in Dart
  • Shake Animation in Flutter
  • Throw Enums in Dart
  • Future Error Test in Flutter
  • Generic URL Retrieval in Dart
  • Custom Error Widget in Flutter
  • Handle Multiple Future Errors in Dart
  • Future Error Handling in Dart
  • String to Toast in Flutter
  • Waiting in Dart
  • Loading Dialog in Flutter
  • Compact Map on Map<K,V> in Dart
  • Query Parameters in Dart
  • Multiple Gradients in Container in Flutter
  • Filter on Stream<List<T>> in Dart
  • Generic Route Arguments in Flutter
  • Generic Dialog in Flutter
  • GitHub API in Flutter
  • ChangeNotifier in Flutter
  • Refresh Indicator in Flutter
  • FlatMap in Dart
  • OrientationBuilder in Flutter
  • Linear Gradient in Flutter
  • Bloc Text Editing Controller in Flutter
  • Blurred TabBar in Flutter
  • Play YouTube in Flutter
  • ListView Background in Flutter
  • Integer to Binary in Dart
  • Split String by Length in Dart
  • Image Tint in Flutter
  • SlideTransition in Flutter
  • Expansion Panels and Lists in Flutter
  • Complete CRUD App in Flutter
  • SQLite Storage in Flutter
  • Circular Progress with Percentage in Flutter
  • Opening URLs in Flutter
  • Commodore 64 Screen in Flutter
  • Animated Lists in Flutter
  • CheckboxListTile in Flutter
  • - Operator on String in Dart
  • Dart Progress for Future<T>
  • Move Widget Shadows with Animation
  • Gallery with Blurred Backgrounds in Flutter
  • Custom Path Clippers in Flutter
  • Frost Effect on Images in Flutter
  • Custom Clippers in Flutter
  • Check if Website is Up or Down in Dart
  • Section Titles on ListView in Flutter
  • Circular Progress in Flutter
  • Displaying Scroll Wheels in Flutter
  • Post Messages to Slack with Dart
  • Unwrap List<T?>? in Dart
  • Avoiding UI Jitters When Switching Widgets in Flutter
  • Detect Redirects in Dart
  • Proportional Constraints in Flutter
  • Displaying Cupertino Action Sheets in Flutter
  • Rotating List<T> in Dart
  • Displaying SnackBars in Flutter
  • Custom Tab Bar Using ToggleButtons in Flutter
  • Hashable Mixins in Dart
  • Flutter Tips and Tricks in Terminal
  • Searching List<List<T>> in Dart
  • Cloning Objects in Dart
  • Color Filters in Flutter
  • Flattening Lists in Dart
  • Managing Duplicates in List<T> in Dart
  • FlatMap and CompactMap in Dart
  • Equality of List<T> in Dart
  • Constants in Dart
  • Displaying Scrollable Bottom Sheets in Flutter
  • YouTube Ad Remover in Dart
  • Fade Between Widgets in Flutter
  • Sort Descriptors in Dart
  • User Sortable Columns and Tables in Flutter
  • Content-Length of List<Uri> in Dart
  • Recursive Dot Notation on Maps in Dart
  • Allow User Selection of Text in Flutter
  • Placing Constraints on Widgets in Flutter
  • Animating Position Changes in Flutter
  • Transitioning Between Widgets in Flutter
  • Doubly Linked Lists in Dart
  • Reordering Items Inside List Views in Flutter
  • Custom Stream Transformers in Dart
  • Expanding Stream Elements in Dart
  • Consume Streams for a Duration in Dart
  • Shortening URLs in Dart
  • LimitedBox Widget as ListView Items in Flutter
  • Generically Convert Anything to Int in Dart
  • Validating URL Certificates in Dart
  • Displaying Popup Menus in Flutter
  • Implementing Drag and Drop in Flutter
  • Dismissing List Items in Flutter
  • Animating Widgets with Ease in Flutter
  • Displaying Tool Tips in Flutter
  • Displaying Assorted Widgets Inside TableView in Flutter
  • Page Indicator with Page View in Flutter
  • Animating and Moving a Floating Action Button in Flutter
  • Fading Network Image Widget in Flutter
  • Transparent Alert Dialogs in Flutter
  • Network Image Size in Dart
  • Animated Icons in Flutter
  • Custom Scroll Views in Flutter
  • Parallax App Bar in Flutter
  • JSON HTTP Requests in Dart
  • URL Timeouts in Dart
  • Detecting URL File Types in Dart
  • Paginated Lists in Dart
  • Requesting DELETE on APIs in Dart
  • Animated Containers in Flutter
  • Hiding Widgets in Flutter
  • Simple Opacity Animation in Flutter
  • Vignette Widget in Flutter
  • Drop Down Button Configuration and Usage in Flutter
  • Expandable List Items in Flutter
  • Infinite Scrolling in Flutter
  • Infinite Arrays in Dart
  • Custom Color Picker Component in Flutter
  • Displaying and Reacting to Switches in Flutter
  • Displaying Bottom Bars in Flutter
  • Displaying Buttons on AppBar in Flutter
  • Displaying Bottom Sheets in Flutter
  • Converting Enums to Radio Buttons in Flutter
  • Check Existence of Websites in Flutter
  • Images inside AlertDialog in Flutter
  • Returning Values from AlertDialog in Flutter
  • Simple Grid View in Flutter
  • Rendering Bullet Points in Flutter
  • Retrying Futures in Flutter
  • Containers as ClipOvals in Flutter
  • Rich Texts in Flutter
  • Wrapping Widgets in Flutter
  • Sweep Gradients in Flutter
  • Stream and StreamBuilder in Flutter
  • Blur Effect in Flutter
  • Convert Enums to Strings in Dart
  • Replacing Text in TextField in Flutter
  • Aspect Ratio in Flutter
  • Zoom and Pan in Flutter
  • Resizing Images in Flutter to Fit Screen Height
  • Validating URLs in Flutter
  • FrameBuilder for Network Images in Flutter
  • Adding Shadow to Icons in Flutter
  • Calculating Median of Lists in Dart
  • Generic Functions with Reduce in Dart
  • Passing Back Data From a Screen to the Previous One in Flutter
  • Flinging an Animation in Flutter
  • Fade Animations in Flutter
  • Throttling User Input in Flutter
  • Censoring TextFields in Flutter
  • Customizing TextButton in Flutter
  • Multiline TextFields in Flutter
  • Filtering TextField Input in Flutter
  • Focusing Manually on TextFields in Flutter
  • Data Streams Over HTTP/HTTPs in Dart
  • Catching Nonexistent Accessors or Methods in Dart
  • Using Expando in Dart
  • Implementing Custom Maps in Dart
  • Dynamically Calling Functions in Dart
  • Factory Constructors in Dart
  • Calculating the Sum of List Items in Dart
  • Removing Duplicate Strings in Lists in Dart (Case-Insensitive)
  • Implementing Range in Dart
  • Converting Lists to Maps in Dart
  • Implementing Hashable in Dart
  • Random Name Generator in Dart
  • Capturing Stack Traces in Dart Exceptions
  • Removing Duplicates from Lists in Dart
  • Optional Spread Operator in Dart
  • Calling Optional Functions in Dart
  • Odd-Even Sort in Dart
  • Implementing Zip and Tuples in Dart
  • Swapping Values in Lists with XOR in Dart
  • Waiting for Multiple Futures in Dart
  • Using Queues as Stacks in Dart
  • Custom Iterators in Dart
  • Iterables as Ranges + Transform in Dart
  • Errors vs Exceptions in Dart
  • Custom Annotations in Dart
  • Classes as Enums in Dart
  • Spread Operator in Collection Literals in Dart
  • StreamBuilder and StreamController in Dart
  • Almost Equal in Dart
  • Enum Associated Values in Dart
  • Implementing Comparable in Dart
  • Implementing Custom Integer Types in Dart
  • Custom Subscripts in Dart
  • Dart List Enumeration with Index
  • Applying Mixins to Other Mixins in Dart
  • Parameter Types in Dart
  • Custom Exceptions in Dart
  • rethrowing Exceptions in Dart
  • mixins and JSON Parsing in Dart
  • mixins vs abstract classes in Dart
  • Drawing Shapes in Flutter with LayoutBuilder, CustomPaint and CustomPainter
  • Generic Type Aliases in Dart
  • Callable Classes in Dart
  • Synchronous Generators in Dart
  • Implicit Interfaces in Dart
  • const Constructors in Dart
  • async-await Over Raw Futures in Dart
  • Initializer List and Default Values as Convenience Intializers in Dart
  • Extract Elements of Certain Type from Lists in Dart
  • Type Promotion in Dart
  • Extract Minimum and Maximum Values in List<num> in Dart
  • Functions as First Class Citizens in Dart

 

LazyStream in Flutter and Dart



import 'dart:developer' as devtools show log;
import 'dart:typed_data' show Uint8List;
import 'package:flutter/services.dart' show NetworkAssetBundle, rootBundle;
import 'package:async/async.dart' show LazyStream;

extension LocalFileData on String {
  Future<Uint8List> localFileData() => rootBundle.load(this).then(
        (byteData) => byteData.buffer.asUint8List(),
      );
}

extension Log on Object {
  void log() => devtools.log(toString());
}

void testIt() async {
  final stream = LazyStream(
    () async {
      final allData = await calculateAllData();
      return getImagesData(allData);
    },
  );

  await for (final data in stream) {
    'Got data, length = ${data.length}'.log();
  }
}

Stream<Uint8List> getImagesData(
  List<Future<Uint8List>> allData,
) async* {
  for (final data in allData) {
    yield await data;
  }
}

Future<List<Future<Uint8List>>> calculateAllData() async {
  final futures = Iterable.generate(
    3,
    (i) => 'images/image_list${i + 1}.txt'
        .localFileData()
        .then((data) => String.fromCharCodes(data)),
  );
  final result = Future.wait(futures);
  final lineSplitter = const LineSplitter();
  List<Future<Uint8List>> allData = [];
  for (final string in await result) {
    final urls = lineSplitter.convert(string);
    for (final url in urls) {
      allData.add(
        NetworkAssetBundle(Uri.parse(url))
            .load(url)
            .then((byteData) => byteData.buffer.asUint8List()),
      );
    }
  }
  return allData;
}

Cancelable APIs in Flutter

import 'dart:developer' as devtools show log;
import 'dart:typed_data' show Uint8List;
import 'package:flutter/services.dart' show NetworkAssetBundle, rootBundle;
import 'package:async/async.dart' show CancelableOperation;

extension Log on Object {
  void log() => devtools.log(toString());
}

extension LocalFileData on String {
  Future<Uint8List> localFileData() => rootBundle.load(this).then(
        (byteData) => byteData.buffer.asUint8List(),
      );
}

CancelableOperation<Uint8List> getImageOperation(String url) =>
    CancelableOperation.fromFuture(
      NetworkAssetBundle(Uri.parse(url))
          .load(url)
          .then((byteData) => byteData.buffer.asUint8List()),
      onCancel: () => 'images/template.png'.localFileData(),
    );

void testIt() async {
  final operation = getImageOperation('http://127.0.0.1:5500/images/1.png');
  final cancelledValue = await operation.cancel();
  final result = await operation.valueOrCancellation(cancelledValue);
  result?.log();
}

Asset Data in Flutter


import 'dart:typed_data' show Uint8List;
import 'package:flutter/services.dart' show rootBundle;
import 'dart:developer' as devtools show log;

extension Log on Object {
  void log() => devtools.log(toString());
}

extension LocalFileData on String {
  Future<Uint8List> localFileData() => rootBundle.load(this).then(
        (byteData) => byteData.buffer.asUint8List(),
      );
}

void testIt() async {
  (await 'images/template.png'.localFileData()).log();
}

API Caching in Flutter


import 'dart:typed_data' show Uint8List;
import 'package:flutter/services.dart' show NetworkAssetBundle;
import 'dart:developer' as devtools show log;
import 'package:async/async.dart' show AsyncMemoizer;

extension Log on Object {
  void log() => devtools.log(toString());
}

@immutable
class GetImageApi {
  final String url;
  final _fetch = AsyncMemoizer<Uint8List>();

  GetImageApi({required this.url});
  Future<Uint8List> fetch() => _fetch.runOnce(
        () => NetworkAssetBundle(Uri.parse(url))
            .load(url)
            .then((byteData) => byteData.buffer.asUint8List()),
      );
}

void testIt() async {
  final api = GetImageApi(url: 'http://127.0.0.1:5500/images/1.png');
  (await api.fetch()).log(); // fetched
  (await api.fetch()).log(); // cached
}

FutureGroup in Dart



mixin FutureConvertible<T> {
  Future<T> toFuture();
}

@immutable
class LoginApi with FutureConvertible<bool> {
  @override
  Future<bool> toFuture() => Future.delayed(
        const Duration(seconds: 1),
        () => true,
      );
}

@immutable
class SignUpApi with FutureConvertible<bool> {
  @override
  Future<bool> toFuture() => Future.delayed(
        const Duration(seconds: 1),
        () => true,
      );
}

extension Flatten on Iterable<bool> {
  bool flatten() => fold(
        true,
        (lhs, rhs) => lhs && rhs,
      );
}

extension Log on Object {
  void log() => devtools.log(toString());
}

Future<bool> startup({
  required bool shouldLogin,
  required bool shouldSignUp,
}) {
  final group = FutureGroup<bool>();
  if (shouldLogin) {
    group.add(LoginApi().toFuture());
  }
  if (shouldSignUp) {
    group.add(SignUpApi().toFuture());
  }
  group.close();
  return group.future.then((bools) => bools.flatten());
}

void testIt() async {
  final success = await startup(
    shouldLogin: true,
    shouldSignUp: false,
  );
  success.log();
}

Flatten Iterable<bool> in Dart


extension Flatten on Iterable<bool> {
  bool flatten() => fold(
        true,
        (lhs, rhs) => lhs && rhs,
      );
}

void testIt() {
  assert([true, false, true].flatten() == false);
  assert([true, true, true].flatten() == true);
  assert([false, false, false].flatten() == false);
  assert([true].flatten() == true);
  assert([false].flatten() == false);
}

Caching Temp Files in Flutter



@immutable
class NetworkImageAsset {
  final String localPath;
  final String url;
  NetworkImageAsset({required int index})
      : localPath = Directory.systemTemp.path + '/$index.png',
        url = 'http://127.0.0.1:5500/images/$index}.png';

  Future<bool> downloadAndSave() => NetworkAssetBundle(Uri.parse(url))
      .load(url)
      .then((byteData) => byteData.buffer.asUint8List())
      .then((data) => File(localPath).writeAsBytes(data).then((_) => true))
      .catchError((_) => false);
}

void testIt() async {
  await Future.forEach(
    Iterable.generate(
      3,
      (i) => NetworkImageAsset(index: i + 1),
    ),
    (NetworkImageAsset asset) => asset.downloadAndSave(),
  );
}

Custom Lists in Dart



import 'dart:developer' as devtools show log;
import 'dart:collection' show ListBase;

class LowercaseList extends ListBase<String> {
  final List<String> _list = [];

  @override
  int get length => _list.length;
  @override
  set length(int newLength) => _list.length = newLength;

  @override
  String operator [](int index) => _list[index].toUpperCase();

  @override
  void operator []=(int index, value) => _list[index] = value;

  @override
  void addAll(Iterable<String> iterable) => _list.addAll(iterable);

  @override
  void add(String element) => _list.add(element);
}

extension Log on Object {
  void log() => devtools.log(toString());
}

void testIt() {
  final myList = LowercaseList();
  myList.addAll(['foo', 'bar', 'baz']);
  myList[0].log(); // FOO
  myList[1].log(); // BAR
  for (final item in myList) {
    item.log(); // FOO, BAR, BAZ
  }
}

Optional Chaining in Dart



@immutable
class Address {
  final String? firstLine;
  final String? secondLine;
  const Address(this.firstLine, this.secondLine);
}

@immutable
class Person {
  final Person? father;
  final Address? address;
  const Person(this.father, this.address);
}

extension GetFathersFirstAddressLine on Person {
  String? get firstAddressLineOfFather => father?.address?.firstLine;
}

MapList in Flutter



extension MapToList<T> on Iterable<T> {
  List<E> mapList<E>(E Function(T) toElement) => 
    map(toElement).toList();
}

Future<bool> in Flutter



Future<bool> uploadImage({
  required File file,
  required String userId,
}) =>
    FirebaseStorage.instance
        .ref(userId)
        .child(const Uuid().v4())
        .putFile(file)
        .then((_) => true)
        .catchError((_) => false);

Async Bloc Init in Flutter



class App extends StatelessWidget {
  const App({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocProvider<AppBloc>(
      create: (context) => AppBloc()..add(const AppEventInitialize()),
      child: MaterialApp(
        title: 'Photo Library',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        debugShowCheckedModeBanner: false,
        home: BlocConsumer<AppBloc, AppState>(
          listener: (context, state) {
            // handle loading
            if (state.isLoading) {
              LoadingScreen().show(
                context: context,
                text: 'Loading...',
              );
            } else {
              LoadingScreen().hide();
            }
            
            ... rest of your code goes here

Firebase Auth Errors in Flutter



const authErrorMapping = {
  'user-not-found': AuthErrorUserNotFound(),
  'project-not-found': AuthErrorProjectNotFound(),
};

@immutable
abstract class AuthError {
  factory AuthError.from(FirebaseAuthException exception) =>
      authErrorMapping[exception.code.toLowerCase().trim()] ??
      const AuthErrorUnknown();
}

@immutable
class AuthErrorUnknown implements AuthError {
  const AuthErrorUnknown();
}

@immutable
class AuthErrorUserNotFound implements AuthError {
  const AuthErrorUserNotFound();
}

@immutable
class AuthErrorProjectNotFound implements AuthError {
  const AuthErrorProjectNotFound();
}

Debug Strings in Flutter


extension IfDebugging on String {
  String? get ifDebugging => kDebugMode ? this : null;
}

class LoginView extends HookWidget {
  const LoginView({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final emailController = useTextEditingController(
      text: 'foo@bar.com'.ifDebugging,
    );
    final passwordController = useTextEditingController(
      text: 'foobarbaz'.ifDebugging,
    );
    
    // rest of your code would be here ...

Keyboard Appearance in Flutter


class LoginView extends HookWidget {
  const LoginView({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Log in'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          children: const [
            TextField(
              keyboardType: TextInputType.emailAddress,
              keyboardAppearance: Brightness.dark,
            ),
            TextField(
              obscureText: true,
              obscuringCharacter: '◉',
            ),
          ],
        ),
      ),
    );
  }
}

Get String Data in Dart


extension ToList on String {
  Uint8List toUint8List() => Uint8List.fromList(codeUnits);
}

final text1Data = 'Foo'.toUint8List();
final text2Data = 'Bar'.toUint8List();

Stream.startWith in Flutter


import 'package:async/async.dart' show StreamGroup;
import 'dart:developer' as devtools show log;

extension Log on Object {
  void log() => devtools.log(toString());
}

extension StartWith<T> on Stream<T> {
  Stream<T> startWith(T value) => StreamGroup.merge([
        this,
        Stream<T>.value(value),
      ]);
}

void testIt() {
  Stream.periodic(const Duration(seconds: 1), (i) => i + 1)
      .startWith(0)
      .take(4)
      .forEach((element) {
    element.log();
  }); // 0, 1, 2, 3
}

Optional Functions in Dart



typedef AppBlocRandomUrlPicker = String Function(Iterable<String> allUrls);

extension RandomElement<T> on Iterable<T> {
  T getRandomElement() => elementAt(
        math.Random().nextInt(length),
      );
}

class AppBloc extends Bloc<AppEvent, AppState> {
  String _pickRandomUrl(Iterable<String> allUrls) => allUrls.getRandomElement();

  AppBloc({
    required Iterable<String> urls,
    AppBlocRandomUrlPicker? urlPicker,
  }) : super(const AppState.empty()) {
    on<LoadNextUrlEvent>(
      (event, emit) {
        emit(
          const AppState(
            isLoading: true,
            data: null,
          ),
        );
        // pick a random URL to load
        final url = (urlPicker ?? _pickRandomUrl)(urls);
        HttpClient().getUrl(Uri.parse(url)); // continue here...
      },
    );
  }
}

AnnotatedRegion in Flutter



class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: AnnotatedRegion<SystemUiOverlayStyle>(
        value: SystemUiOverlayStyle.dark,
        child: Column(
          mainAxisSize: MainAxisSize.max,
          children: [
            Expanded(child: Container(color: Colors.blue)),
            Expanded(child: Container(color: Colors.yellow)),
          ],
        ),
      ),
    );
  }
}

Unordered Map Equality in Dart



import 'package:collection/collection.dart';
import 'dart:developer' as devtools show log;

extension Log on Object {
  void log() => devtools.log(toString());
}

extension UnorderedEquality<K, V> on Map<K, V> {
  bool isEqualTo(Map<K, V> other) =>
      const DeepCollectionEquality.unordered().equals(this, other);
}

void testIt() {
  final dict1 = {
    'name': 'foo',
    'age': 20,
    'values': ['foo', 'bar'],
  };
  final dict2 = {
    'age': 20,
    'name': 'foo',
    'values': ['bar', 'foo'],
  };
  dict1.isEqualTo(dict2).log(); // true
}

Iterable to ListView in Flutter



extension ToListView<T> on Iterable<T> {
  Widget toListView() => IterableListView(
        iterable: this,
      );
}

class IterableListView<T> extends StatelessWidget {
  final Iterable<T> iterable;
  const IterableListView({
    Key? key,
    required this.iterable,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: iterable.length,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text(
            iterable.elementAt(index).toString(),
          ),
        );
      },
    );
  }
}

@immutable
class Person {
  final String name;
  final int age;
  const Person({required this.name, required this.age});
  @override
  String toString() => '$name, $age years old';
}

const persons = [
  Person(name: 'Foo', age: 20),
  Person(name: 'Bar', age: 30),
  Person(name: 'Baz', age: 40),
];

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home Page'),
      ),
      body: persons.toListView(),
    );
  }
}

Password Mask in Flutter



class PasswordTextField extends StatelessWidget {
  const PasswordTextField({
    Key? key,
    required this.passwordController,
  }) : super(key: key);

  final TextEditingController passwordController;

  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: passwordController,
      obscureText: true,
      obscuringCharacter: '◉',
      decoration: const InputDecoration(
        hintText: 'Enter your password here...',
      ),
    );
  }
}

Fast Object.toString() in Dart



@immutable
class AppState {
  final bool isLoading;
  final Object? loginError;
  final String? loginHandle;
  final Iterable<String>? fetchedNotes;

  @override
  String toString() => {
        'isLoading': isLoading,
        'loginError': loginError,
        'loginHandle': loginHandle,
        'fetchedNotes': fetchedNotes
      }.toString();

  const AppState({
    required this.isLoading,
    required this.loginError,
    required this.loginHandle,
    required this.fetchedNotes,
  });
}

Copying Bloc State in Flutter



@immutable
class AppState {
  final bool isLoading;
  final LoginHandle? loginHandle;
  final Iterable<Note>? fetchedNotes;

  const AppState.empty()
      : isLoading = false,
        loginHandle = null,
        fetchedNotes = null;

  const AppState({
    required this.isLoading,
    required this.loginHandle,
    required this.fetchedNotes,
  });

  AppState copiedWith({
    bool? isLoading,
    LoginHandle? loginHandle,
    Iterable<Note>? fetchedNotes,
  }) =>
      AppState(
        isLoading: isLoading ?? this.isLoading,
        loginHandle: loginHandle ?? this.loginHandle,
        fetchedNotes: fetchedNotes ?? this.fetchedNotes,
      );
}

Iterable Subscripts in Dart

// Free Flutter Course 💙 https://linktr.ee/vandadnp
// Want to support my work 🤝? https://buymeacoffee.com/vandad

import 'dart:developer' as devtools show log;

extension Log on Object? {
  void log() => devtools.log(toString());
}

extension Subscript<T> on Iterable<T> {
  T? operator [](int index) => length > index ? elementAt(index) : null;
}

void testIt() {
  Iterable.generate(10, (i) => i + 1)[0].log(); // 1
  Iterable.generate(1, (i) => i)[2].log(); // null
  Iterable.generate(10, (i) => i + 1)[9].log(); // 10
  Iterable.generate(0, (i) => i)[0].log(); // null
}

useState in Flutter Hooks



import 'package:flutter_hooks/flutter_hooks.dart';
import 'dart:math' show min;

@immutable
class VirtualTab {
  final Icon icon;
  final String text;

  const VirtualTab({
    required this.icon,
    required this.text,
  });
}

const tabs = [
  VirtualTab(
    icon: Icon(Icons.picture_as_pdf),
    text: 'All PDF files',
  ),
  VirtualTab(
    icon: Icon(Icons.ac_unit_outlined),
    text: 'Data page',
  ),
  VirtualTab(
    icon: Icon(Icons.person),
    text: 'Profile page',
  ),
];

class HomePage extends HookWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final tabCount = useState(1);
    return DefaultTabController(
      length: tabCount.value,
      initialIndex: tabCount.value - 1,
      child: Scaffold(
        appBar: AppBar(
          bottom: TabBar(
            tabs: tabs
                .take(tabCount.value)
                .map((tab) => Tab(icon: tab.icon))
                .toList(),
          ),
        ),
        body: CustomTabBarView(tabCount: tabCount),
      ),
    );
  }
}

class CustomTabBarView extends StatelessWidget {
  const CustomTabBarView({
    Key? key,
    required this.tabCount,
  }) : super(key: key);

  final ValueNotifier<int> tabCount;

  @override
  Widget build(BuildContext context) {
    return TabBarView(
      children: tabs
          .take(tabCount.value)
          .map(
            (tab) => Padding(
              padding: const EdgeInsets.all(8.0),
              child: Column(
                children: [
                  Text(tab.text),
                  TextButton(
                    onPressed: () {
                      final newLength = min(
                        tabs.length,
                        tabCount.value + 1,
                      );
                      tabCount.value = newLength;
                    },
                    child: const Text('Create next tab'),
                  )
                ],
              ),
            ),
          )
          .toList(),
    );
  }
}

 

Folding Iterables in Dart



import 'dart:developer' as devtools show log;

extension Log on Object {
  void log() => devtools.log(toString());
}

void testIt() {
  final values = ['foo', 'bar', 'baz', '1.0'];

  values.fold<int>(0, (pe, e) => pe + e.length); // 12

  values.fold<String>('', (pe, e) => '$pe$e'); // foobarbaz1.0

  values.fold<Map<String, int>>(
    {},
    (pe, e) => pe..addAll(<String, int>{e: e.length}),
  ).log(); // {foo: 3, bar: 3, baz: 3, 1.0: 3}

  values.fold<double>(
    0.0,
    (pe, e) => pe + (double.tryParse(e) ?? 0.0),
  ); // 1.0
}

Custom Iterables in Dart



class Address with IterableMixin {
  final String line1;
  final String line2;
  final String postCode;

  Address({
    required this.line1,
    required this.line2,
    required this.postCode,
  });

  @override
  Iterator<String> get iterator => [line1, line2, postCode].iterator;
}

void testIt() {
  final address = Address(
    line1: 'Foo bar avenue, #10',
    line2: 'Baz street',
    postCode: '123456',
  );
  for (final line in address) {
    devtools.log(line);
  }
}

Class Clusters in Dart



enum AnimalType { dog, cat }

@immutable
abstract class Animal {
  const Animal();
  factory Animal.fromType(AnimalType type) {
    switch (type) {
      case AnimalType.dog:
        return const Dog();
      case AnimalType.cat:
        return const Cat();
    }
  }
  void makeNoise();
}

@immutable
class Dog extends Animal {
  const Dog();
  @override
  void makeNoise() => 'Woof'.log();
}

@immutable
class Cat extends Animal {
  const Cat();
  @override
  void makeNoise() => 'Meow'.log();
}

void testIt() {
  final cat = Animal.fromType(AnimalType.cat);
  cat.makeNoise();
  final dog = Animal.fromType(AnimalType.dog);
  dog.makeNoise();
}

Iterable +/- in Dart



extension AddRemoveItems<T> on Iterable<T> {
  Iterable<T> operator +(T other) => followedBy([other]);
  Iterable<T> operator -(T other) => where((element) => element != other);
}

void testIt() {
  final values = ['foo', 'bar']
    .map((e) => e.toUpperCase()) + 'BAZ';
  values.log(); // (FOO, BAR, BAZ)
  (values - 'BAZ').log(); // (FOO, BAR)
}

Periodic Streams in Dart



import 'dart:io';
import 'package:flutter/material.dart';
import 'dart:convert';
import 'dart:developer' as devtools show log;

extension Log on Object {
  void log() => devtools.log(toString());
}

@immutable
class Person {
  final String name;
  final int age;

  const Person({
    required this.name,
    required this.age,
  });

  Person.fromJson(Map<String, dynamic> json)
      : name = json["name"] as String,
        age = json["age"] as int;

  @override
  String toString() => 'Person ($name, $age years old)';
}

mixin ListOfThingsAPI<T> {
  Future<Iterable<T>> get(String url) => HttpClient()
      .getUrl(Uri.parse(url))
      .then((req) => req.close())
      .then((resp) => resp.transform(utf8.decoder).join())
      .then((str) => json.decode(str) as List<dynamic>)
      .then((list) => list.cast());
}

class GetPeople with ListOfThingsAPI<Map<String, dynamic>> {
  Future<Iterable<Person>> getPeople(url) => get(url).then(
        (jsons) => jsons.map(
          (json) => Person.fromJson(json),
        ),
      );
}

Stream<dynamic> every(Duration duration) => Stream.periodic(duration);

extension IntToDuration on int {
  Duration get seconds => Duration(seconds: this);
}

void testIt() async {
  await for (final people in every(3.seconds).asyncExpand(
    (_) => GetPeople()
        .getPeople('http://127.0.0.1:5500/apis/people1.json')
        .asStream(),
  )) {
    people.log();
  }
}

/* people1.json
[
    {
        "name": "Foo 1",
        "age": 20
    },
    {
        "name": "Bar 1",
        "age": 30
    }
]
*/

EmptyOnError in Dart



import 'dart:io';
import 'package:flutter/material.dart';
import 'dart:convert';
import 'dart:developer' as devtools show log;

extension Log on Object {
  void log() => devtools.log(toString());
}

@immutable
class Person {
  final String name;
  final int age;

  const Person({
    required this.name,
    required this.age,
  });

  Person.fromJson(Map<String, dynamic> json)
      : name = json["name"] as String,
        age = json["age"] as int;

  @override
  String toString() => 'Person ($name, $age years old)';
}

const people1Url = 'http://127.0.0.1:5500/apis/people11.json';
const people2Url = 'http://127.0.0.1:5500/apis/people2.json';

extension EmptyOnError<E> on Future<List<Iterable<E>>> {
  Future<List<Iterable<E>>> emptyOnError() => catchError(
        (_, __) => List<Iterable<E>>.empty(),
      );
}

Future<Iterable<Person>> parseJson(String url) => HttpClient()
    .getUrl(Uri.parse(url))
    .then((req) => req.close())
    .then((resp) => resp.transform(utf8.decoder).join())
    .then((str) => json.decode(str) as List<dynamic>)
    .then((json) => json.map((e) => Person.fromJson(e)));

void testIt() async {
  final persons = await Future.wait([
    parseJson(people1Url),
    parseJson(people2Url),
  ]).emptyOnError();
  persons.log();
}

Stream<T> Initial Value in Flutter



import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

void main() {
  runApp(
    MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      debugShowCheckedModeBanner: false,
      home: const HomePage(),
    ),
  );
}

const url = 'https://bit.ly/3x7J5Qt';

class HomePage extends HookWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    late final StreamController<double> controller;
    controller = useStreamController<double>(onListen: () {
      controller.sink.add(0.0);
    });

    return Scaffold(
      appBar: AppBar(
        title: const Text('Home page'),
      ),
      body: StreamBuilder<double>(
          stream: controller.stream,
          builder: (context, snapshot) {
            if (!snapshot.hasData) {
              return const CircularProgressIndicator();
            } else {
              final rotation = snapshot.data ?? 0.0;
              return GestureDetector(
                onTap: () {
                  controller.sink.add(rotation + 10.0);
                },
                child: RotationTransition(
                  turns: AlwaysStoppedAnimation(rotation / 360.0),
                  child: Center(
                    child: Image.network(url),
                  ),
                ),
              );
            }
          }),
    );
  }
}

Double.normalize in Dart



import 'dart:developer' as devtools show log;

extension Normalize on double {
  double normalized(
    double selfRangeMin,
    double selfRangeMax, [
    double normalizedRangeMin = 0.0,
    double normalizedRangeMax = 1.0,
  ]) =>
      (normalizedRangeMax - normalizedRangeMin) *
          ((this - selfRangeMin) / (selfRangeMax - selfRangeMin)) +
      normalizedRangeMin;
}

extension Log on Object {
  void log() => devtools.log(toString());
}

void testIt() async {
  2.0.normalized(0, 2.0).log(); // 1.0
  4.0.normalized(0, 8.0).log(); // 0.5
  5.0.normalized(4.0, 6.0, 10.0, 20.0).log(); // 15
}

Hide Sensitive Information in Flutter

 



import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

void main() {
  runApp(
    MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      debugShowCheckedModeBanner: false,
      home: const HomePage(),
    ),
  );
}

class HomePage extends HookWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final state = useAppLifecycleState();
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home Page'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Opacity(
          opacity: state == AppLifecycleState.resumed ? 1.0 : 0.0,
          child: Container(
            decoration: BoxDecoration(
              boxShadow: [
                BoxShadow(
                  blurRadius: 10,
                  color: Colors.black.withAlpha(100),
                  spreadRadius: 10,
                ),
              ],
            ),
            child: Image.asset('assets/card.png'),
          ),
        ),
      ),
    );
  }
}

Iterable.compactMap in Dart



import 'dart:developer' as devtools show log;

extension Log on Object {
  void log() => devtools.log(toString());
}

extension CompactMap<T> on Iterable<T?> {
  Iterable<T> compactMap<E>([
    E? Function(T?)? transform,
  ]) =>
      map(transform ?? (e) => e).where((e) => e != null).cast();
}

const list = ['Hello', null, 'World'];

void testIt() {
  list.log(); // [Hello, null, World]
  list.compactMap().log(); // [Hello, World]
  list.compactMap((e) => e?.toUpperCase()).log(); // [HELLO, WORLD]
}

useEffect in Flutter Hooks



import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

void main() {
  runApp(
    MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      debugShowCheckedModeBanner: false,
      home: const HomePage(),
    ),
  );
}

class HomePage extends HookWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final controller = useTextEditingController();
    final text = useState('');
    useEffect(
      () {
        void listener() {
          text.value = controller.text;
        }

        controller.addListener(listener);
        return () => controller.removeListener(listener);
      },
      [controller],
    );
    return Scaffold(
      body: Column(
        children: [
          TextField(
            controller: controller,
          ),
          Text('You typed ${text.value}')
        ],
      ),
    );
  }
}

Merging Streams in Dart



import 'package:async/async.dart' show StreamGroup;
import 'dart:developer' as devtools show log;

extension Log on Object {
  void log() => devtools.log(toString());
}

void testIt() async {
  final streams = Iterable.generate(
    3,
    (i) => Stream.periodic(
      const Duration(seconds: 1),
      (_) => 'Stream $i: ${DateTime.now().toIso8601String()}',
    ).take(i + 1),
  );
  await for (final now in StreamGroup.merge(streams)) {
    now.log();
  }
}

Isolate Stream in Dart



Stream<String> getMessages() {
  final rp = ReceivePort();
  return Isolate.spawn(_getMessages, rp.sendPort)
      .asStream()
      .asyncExpand((_) => rp)
      .takeWhile((element) => element is String)
      .cast();
}

void _getMessages(SendPort sp) async {
  await for (final now in Stream.periodic(
    const Duration(seconds: 1),
    (_) => DateTime.now().toIso8601String(),
  ).take(10)) {
    sp.send(now);
  }
  Isolate.exit(sp);
}

void testIt() async {
  await for (final msg in getMessages()) {
    msg.log();
  }
}

Network Image Retry in Flutter



@immutable
class RetryStrategy {
  final bool shouldRetry;
  final Duration waitBeforeRetry;
  const RetryStrategy({
    required this.shouldRetry,
    required this.waitBeforeRetry,
  });
}

typedef Retrier = RetryStrategy Function(String url, Object error);

class NetworkImageWithRetry extends StatelessWidget {
  final Widget loadingWidget;
  final Widget errorWidget;
  final String url;
  final Retrier retrier;

  final _controller = StreamController<Uint8List>.broadcast();

  NetworkImageWithRetry({
    Key? key,
    required this.url,
    required this.retrier,
    required this.loadingWidget,
    required this.errorWidget,
  }) : super(key: key);

  void getData() async {
    while (true == true) {
      try {
        final networkAsset = NetworkAssetBundle(Uri.parse(url));
        final loaded = await networkAsset.load(url);
        final bytes = loaded.buffer.asUint8List();
        _controller.sink.add(bytes);
        break;
      } catch (e) {
        final strategy = retrier(url, e);
        if (strategy.shouldRetry) {
          await Future.delayed(strategy.waitBeforeRetry);
        } else {
          _controller.sink.addError(e);
          break;
        }
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    getData();
    return StreamBuilder(
      stream: _controller.stream,
      builder: (context, AsyncSnapshot<Uint8List> snapshot) {
        if (snapshot.hasError) {
          return errorWidget;
        } else {
          final data = snapshot.data;
          if (snapshot.hasData && data != null) {
            return Image.memory(data);
          } else {
            return loadingWidget;
          }
        }
      },
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Image Retry'),
      ),
      body: NetworkImageWithRetry(
        url: 'https://bit.ly/3qYOtDm',
        errorWidget: const Text('Got an error!'),
        loadingWidget: const Text('Loading...'),
        retrier: (url, error) {
          return RetryStrategy(
            shouldRetry: error is! FlutterError,
            waitBeforeRetry: const Duration(seconds: 1),
          );
        },
      ),
    );
  }
}

Reusable APIs in Flutter

import 'dart:io';
import 'package:flutter/material.dart';
import 'dart:developer' as devtools show log;
import 'dart:convert' show utf8;
import 'package:meta/meta.dart' show useResult;

extension Log on Object {
  void log() => devtools.log(toString());
}

extension GetOnUri on Object {
  Future<HttpClientResponse> getUrl(
    String url,
  ) =>
      HttpClient()
          .getUrl(
            Uri.parse(
              url,
            ),
          )
          .then((req) => req.close());
}

mixin CanMakeGetCall {
  String get url;
  @useResult
  Future<String> getString() => getUrl(url).then(
        (response) => response
            .transform(
              utf8.decoder,
            )
            .join(),
      );
}

@immutable
class GetPeople with CanMakeGetCall {
  const GetPeople();
  @override
  String get url => 'http://127.0.0.1:5500/apis/people.json';
}

void testIt() async {
  final people = await const GetPeople().getString();
  devtools.log(people);
}

ListTile Shadow in Flutter



enum Currency { dollars }

extension Title on Currency {
  String get title {
    switch (this) {
      case Currency.dollars:
        return '\$';
    }
  }
}

@immutable
class Item {
  final IconData icon;
  final String name;
  final double price;
  final Currency currency;

  const Item({
    required this.icon,
    required this.name,
    required this.price,
    required this.currency,
  });

  String get description => '$price${currency.title}';
}

const items = [
  Item(
    icon: Icons.camera_alt,
    name: 'Camera',
    price: 300,
    currency: Currency.dollars,
  ),
  Item(
    icon: Icons.house,
    name: 'House',
    price: 1000000,
    currency: Currency.dollars,
  ),
  Item(
    icon: Icons.watch,
    name: 'Smart Watch',
    price: 200,
    currency: Currency.dollars,
  ),
];

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home Page'),
      ),
      body: ListView.builder(
        itemCount: items.length,
        itemBuilder: (_, index) {
          return ItemTile(
            item: items[index],
          );
        },
      ),
    );
  }
}

class ItemTile extends StatelessWidget {
  final Item item;
  const ItemTile({Key? key, required this.item}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Stack(
        children: [
          const TileBackground(),
          CustomTile(item: item),
        ],
      ),
    );
  }
}

class CustomTile extends StatelessWidget {
  final Item item;
  const CustomTile({
    Key? key,
    required this.item,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 7.0),
      child: Container(
        decoration: customDecoration(),
        child: ListTile(
          leading: Icon(
            item.icon,
            color: Colors.white,
          ),
          title: Text(item.name),
          subtitle: Text(item.description),
        ),
      ),
    );
  }
}

BoxDecoration customDecoration() {
  return BoxDecoration(
    color: const Color.fromARGB(255, 0x7d, 0xcf, 0xff),
    borderRadius: BorderRadius.circular(10.0),
    border: Border.all(
      color: Colors.black,
      width: 2.0,
    ),
  );
}

class TileBackground extends StatelessWidget {
  const TileBackground({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Positioned.fill(
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 6.0),
        child: Container(
          decoration: BoxDecoration(
            color: const Color.fromARGB(255, 202, 255, 127),
            borderRadius: BorderRadius.circular(10.0),
            border: Border.all(
              color: Colors.black,
              width: 2.0,
            ),
          ),
        ),
      ),
    );
  }
}

Transparent AppBar in Flutter



const images = [
  'https://bit.ly/3x7J5Qt',
  'https://bit.ly/3ywI8l6',
  'https://bit.ly/36fNNj9',
  'https://bit.ly/3jOueGG',
  'https://bit.ly/3qYOtDm',
  'https://bit.ly/3wt11Ec',
  'https://bit.ly/3yvFg7X',
  'https://bit.ly/3ywzOla',
  'https://bit.ly/3wnASpW',
  'https://bit.ly/3jXSDto',
];

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      extendBodyBehindAppBar: true,
      appBar: AppBar(
        elevation: 0.0,
        backgroundColor: Colors.blueAccent.withAlpha(200),
        title: const Text('Transparent App Bar in Flutter'),
      ),
      body: const ImagesScrollView(),
    );
  }
}

class ImagesScrollView extends StatelessWidget {
  const ImagesScrollView({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      padding: const EdgeInsets.only(top: 80.0),
      child: Padding(
        padding: const EdgeInsets.only(
          top: 40.0,
          left: 10.0,
          right: 10.0,
        ),
        child: Column(
          children: images
              .map((url) => ElevatedNetworkImage(url: url))
              .expand(
                (img) => [
                  img,
                  const SizedBox(height: 30.0),
                ],
              )
              .toList(),
        ),
      ),
    );
  }
}

class ElevatedNetworkImage extends StatelessWidget {
  final String url;
  const ElevatedNetworkImage({Key? key, required this.url}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return PhysicalShape(
      color: Colors.white,
      clipper: Clipper(),
      elevation: 20.0,
      clipBehavior: Clip.none,
      shadowColor: Colors.white.withAlpha(200),
      child: CutEdges(
        child: Image.network(url),
      ),
    );
  }
}

class Clipper extends CustomClipper<Path> {
  static const variance = 0.2;
  static const reverse = 1.0 - variance;

  @override
  Path getClip(Size size) {
    final path = Path();

    path.moveTo(0.0, size.height * Clipper.variance);
    path.lineTo(size.width * Clipper.variance, 0.0);
    path.lineTo(size.width, 0.0);
    path.lineTo(size.width, size.height * Clipper.reverse);
    path.lineTo(size.width * Clipper.reverse, size.height);
    path.lineTo(0.0, size.height);
    path.lineTo(0.0, size.height * Clipper.variance);
    path.close();
    return path;
  }

  @override
  bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}

class CutEdges extends StatelessWidget {
  final Widget child;

  const CutEdges({Key? key, required this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ClipPath(
      clipper: Clipper(),
      child: child,
    );
  }
}

Constructors on Abstract Classes in Dart



import 'dart:developer' as devtools show log;

extension Log on Object {
  void log() => devtools.log(toString());
}

enum Type { dog, cat }

abstract class CanRun {
  final Type type;
  const CanRun({required this.type});
}

class Cat extends CanRun {
  const Cat() : super(type: Type.cat);
}

class Dog extends CanRun {
  const Dog() : super(type: Type.dog);
}

@useResult in Dart



import 'package:meta/meta.dart' show useResult;

class Person {
  final String firstName;
  final String lastName;

  const Person({
    required this.firstName,
    required this.lastName,
  });

  @useResult
  String fullName() => '$firstName $lastName';
}

void printFullName() {
  const Person(
    firstName: 'Foo',
    lastName: 'Bar',
  ).fullName();
}

@mustCallSuper in Dart



class Animal {
  @mustCallSuper
  void run() {}
}

class Dog extends Animal {
  @override
  void run() {}
}

Object.hash in Dart



class BreadCrumb {
  final bool isActive;
  final String name;
  BreadCrumb({
    required this.isActive,
    required this.name,
  });
  BreadCrumb activated() => BreadCrumb(
        isActive: true,
        name: name,
      );
  @override
  bool operator ==(covariant BreadCrumb other) =>
      isActive == other.isActive && name == other.name;

  @override
  int get hashCode => Object.hash(isActive, name);
}

Expanded Equally in Flutter



import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      debugShowCheckedModeBanner: false,
      home: const HomePage(),
    ),
  );
}

extension ExpandEqually on Iterable<Widget> {
  Iterable<Widget> expandedEqually() => map(
        (w) => Expanded(
          flex: 1,
          child: w,
        ),
      );
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home Page'),
      ),
      body: Column(
        children: [
          Row(
            mainAxisSize: MainAxisSize.max,
            children: [
              Container(
                height: 200,
                color: Colors.yellow,
              ),
              Container(
                height: 200,
                color: Colors.blue,
              ),
            ].expandedEqually().toList(),
          )
        ],
      ),
    );
  }
}

Random Iterable Value in Dart



import 'dart:math' as math show Random;

extension RandomElement<T> on Iterable<T> {
  T getRandomElement() => elementAt(
        math.Random().nextInt(length),
      );
}

final colors = [Colors.blue, Colors.red, Colors.brown];

class HomePage extends StatelessWidget {
  final color = ValueNotifier<MaterialColor>(
    colors.getRandomElement(),
  );
  HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('List.Random in Flutter'),
      ),
      body: ColorPickerButton(color: color),
    );
  }
}

class ColorPickerButton extends StatelessWidget {
  final ValueNotifier<MaterialColor> color;

  const ColorPickerButton({
    Key? key,
    required this.color,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<Color>(
      valueListenable: color,
      builder: (context, value, child) {
        return Padding(
          padding: const EdgeInsets.all(8.0),
          child: CenteredTight(
            child: TextButton(
              style: TextButton.styleFrom(backgroundColor: value),
              onPressed: () {
                color.value = colors.getRandomElement();
              },
              child: const Text(
                'Change color',
                style: TextStyle(
                  fontSize: 30,
                  color: Colors.white,
                ),
              ),
            ),
          ),
        );
      },
    );
  }
}

Hardcoded Strings in Flutter



extension Hardcoded on String {
  String get hardcoded => '$this 🧨';
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(
          'My hardcoded string'.hardcoded,
        ),
      ),
      body: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text('String in body'.hardcoded),
        ],
      ),
    );
  }
}

Manually Scroll in List View in Flutter

// Free Flutter Course 💙 https://linktr.ee/vandadnp

import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';

class HomePage extends StatelessWidget {
  final _controller = ItemScrollController();
  HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Testing'),
      ),
      body: ScrollablePositionedList.builder(
        itemScrollController: _controller,
        itemCount: allImages.length + 1,
        itemBuilder: (context, index) {
          if (index == 0) {
            return IndexSelector(
              count: allImages.length,
              onSelected: (index) {
                _controller.scrollTo(
                  index: index + 1,
                  duration: const Duration(milliseconds: 370),
                );
              },
            );
          } else {
            return ImageWithTitle(index: index);
          }
        },
      ),
    );
  }
}

class ImageWithTitle extends StatelessWidget {
  final int index;
  const ImageWithTitle({
    Key? key,
    required this.index,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(
          'Image $index',
          style: const TextStyle(fontSize: 30.0),
        ),
        Image.network(allImages.elementAt(index - 1)),
      ],
    );
  }
}

typedef OnIndexSelected = void Function(int index);

class IndexSelector extends StatelessWidget {
  final int count;
  final OnIndexSelected onSelected;
  final String prefix;
  const IndexSelector({
    Key? key,
    required this.count,
    required this.onSelected,
    this.prefix = 'Image',
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      scrollDirection: Axis.horizontal,
      child: Row(
        children: Iterable.generate(
          count,
          (index) => TextButton(
            onPressed: () {
              onSelected(index);
            },
            child: Text('$prefix ${index + 1}'),
          ),
        ).toList(),
      ),
    );
  }
}

const imageUrls = [
  'https://bit.ly/3ywI8l6',
  'https://bit.ly/36fNNj9',
  'https://bit.ly/3jOueGG',
  'https://bit.ly/3qYOtDm',
  'https://bit.ly/3wt11Ec',
  'https://bit.ly/3yvFg7X',
  'https://bit.ly/3ywzOla',
  'https://bit.ly/3wnASpW',
  'https://bit.ly/3jXSDto',
];

AsyncSnapshot to Widget in Flutter



import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

void main() {
  runApp(
    MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomePage(),
    ),
  );
}

final future = Future<String>.delayed(
  const Duration(seconds: 3),
  () => 'Hello world',
);

typedef ResolveToWidget<T> = Widget Function(
  ConnectionState connectionState,
  AsyncSnapshot<T> snapshot,
);

extension Materialize on AsyncSnapshot {
  Widget materialize(ResolveToWidget f) => f(
        connectionState,
        this,
      );
}

class HomePage extends HookWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Hooks'),
      ),
      body: useFuture(future).materialize((connectionState, snapshot) {
        switch (connectionState) {
          case ConnectionState.done:
            return Text(snapshot.data ?? '');
          default:
            return const CircularProgressIndicator();
        }
      }),
    );
  }
}

Breadcrumbs in Flutter



@immutable
class BreadCrumbPath {
  final String title;
  final bool isActive;
  const BreadCrumbPath({
    required this.title,
    required this.isActive,
  });
  BreadCrumbPath activated() {
    return BreadCrumbPath(
      title: title,
      isActive: true,
    );
  }

  @override
  String toString() => title;
}

class BreatCrumbPathView extends StatelessWidget {
  final BreadCrumbPath path;

  const BreatCrumbPathView({
    Key? key,
    required this.path,
  }) : super(key: key);
  @override
  Widget build(BuildContext context) {
    final title = path.isActive ? '${path.title} →' : path.title;
    return Padding(
      padding: const EdgeInsets.all(2.0),
      child: Text(
        title,
        style: TextStyle(
          height: 1.0,
          fontSize: 20.0,
          color: path.isActive ? Colors.blueAccent : Colors.black,
        ),
      ),
    );
  }
}

typedef OnBreadCrumbPathTapped = void Function(BreadCrumbPath path);

class BreadCrumbView extends StatelessWidget {
  final OnBreadCrumbPathTapped onTapped;
  final Stream<List<BreadCrumbPath>> paths;
  const BreadCrumbView({
    Key? key,
    required this.paths,
    required this.onTapped,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<List<BreadCrumbPath>>(
      stream: paths,
      builder: (context, snapshot) {
        final List<Widget> views;
        switch (snapshot.connectionState) {
          case ConnectionState.waiting:
          case ConnectionState.active:
            final paths = snapshot.data ?? [];
            final views = paths
                .map(
                  (path) => GestureDetector(
                    onTap: () => onTapped(path),
                    child: BreatCrumbPathView(path: path),
                  ),
                )
                .toList();
            return Wrap(
              spacing: 4.0,
              children: views,
              alignment: WrapAlignment.start,
              crossAxisAlignment: WrapCrossAlignment.center,
            );
          default:
            return Wrap();
        }
      },
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  List<BreadCrumbPath> _paths = [];
  late final TextEditingController _textController;
  late final StreamController<List<BreadCrumbPath>> _pathsController;

  @override
  void initState() {
    _pathsController = StreamController<List<BreadCrumbPath>>.broadcast(
      onListen: () {
        _pathsController.add(_paths);
      },
    );
    _textController = TextEditingController();
    super.initState();
  }

  @override
  void dispose() {
    _textController.dispose();
    _pathsController.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Breadcrumb in Flutter'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            BreadCrumbView(
              paths: _pathsController.stream,
              onTapped: (path) async {
                await showBreadCrumbPathTappedDialog(
                  context,
                  path,
                );
              },
            ),
            TextField(
              controller: _textController,
              textAlign: TextAlign.center,
              decoration: const InputDecoration(
                hintText: 'Enter a new path here',
              ),
            ),
            TextButton(
              onPressed: () {
                _paths = [
                  ..._paths.map((p) => p.activated()),
                  BreadCrumbPath(
                    title: _textController.text,
                    isActive: false,
                  ),
                ];
                _pathsController.add(_paths);
                _textController.clear();
              },
              child: const Center(
                child: Text('Add new path'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Future<void> showBreadCrumbPathTappedDialog(
  BuildContext context,
  BreadCrumbPath path,
) {
  return showDialog(
    context: context,
    builder: (context) {
      return AlertDialog(
        content: Text('You tapped on $path'),
        actions: [
          TextButton(
            onPressed: () {
              Navigator.of(context).pop();
            },
            child: const Text('OK'),
          ),
        ],
      );
    },
  );
}

Unique Map Values in Dart



import 'dart:developer' as devtools show log;

extension ContainsDuplicateValues on Map {
  bool get containsDuplicateValues => 
    {...values}.length != values.length;
}

extension Log on Object {
  void log() => devtools.log(toString());
}

const people1 = {
  1: 'Foo',
  2: 'Bar',
};
const people2 = {
  1: 'Foo',
  2: 'Foo',
};

void testIt() {
  people1.containsDuplicateValues.log(); // false
  people2.containsDuplicateValues.log(); // true
}

Smart Quotes/Dashes in Flutter



class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Smart Quotes/Dashes in Flutter'),
      ),
      body: const Padding(
        padding: EdgeInsets.all(16.0),
        child: TextField(
          smartQuotesType: SmartQuotesType.disabled,
          smartDashesType: SmartDashesType.disabled,
          maxLines: null,
        ),
      ),
    );
  }
}

Haptic Feedback in Flutter



class CenteredTight extends StatelessWidget {
  final Widget child;
  const CenteredTight({
    Key? key,
    required this.child,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [child],
    );
  }
}

class FullscreenImage extends StatefulWidget {
  final String imageUrl;
  const FullscreenImage({Key? key, required this.imageUrl}) : super(key: key);

  @override
  State<FullscreenImage> createState() => _FullscreenImageState();
}

class _FullscreenImageState extends State<FullscreenImage> {
  var shouldDisplayAppbar = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: shouldDisplayAppbar ? AppBar(title: const Text('Image')) : null,
      body: GestureDetector(
        onTap: () {
          setState(() => shouldDisplayAppbar = !shouldDisplayAppbar);
        },
        child: Image.network(
          widget.imageUrl,
          alignment: Alignment.center,
          width: double.infinity,
          height: double.infinity,
          fit: BoxFit.cover,
        ),
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Haptic Feedback in Flutter'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: CenteredTight(
          child: FractionallySizedBox(
            heightFactor: 0.7,
            child: GestureDetector(
              onLongPress: () async {
                await HapticFeedback.lightImpact();
                Navigator.of(context).push(
                  MaterialPageRoute(
                    builder: (context) {
                      return const FullscreenImage(
                        imageUrl: imageUrl,
                      );
                    },
                  ),
                );
              },
              child: Image.network(imageUrl),
            ),
          ),
        ),
      ),
    );
  }
}

Localization Delegates in Flutter



import 'package:flutter_gen/gen_l10n/app_localizations.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(
    MaterialApp(
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: BlocProvider<AuthBloc>(
        create: (context) => AuthBloc(FirebaseAuthProvider()),
        child: const HomePage(),
      ),
      routes: {
        createOrUpdateNoteRoute: (context) => const CreateUpdateNoteView(),
      },
    ),
  );
}

Extending Functions in Dart



import 'dart:developer' as devtools show log;

extension ToTextButton on VoidCallback {
  TextButton toTextButton(String title) {
    return TextButton(
      onPressed: this,
      child: Text(title),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Extensions in Flutter'),
      ),
      body: () {
        devtools.log('I am pressed');
      }.toTextButton('Press me'),
    );
  }
}

Paginated ListView in Flutter



@immutable
class Season {
  final String name;
  final String imageUrl;
  const Season({required this.name, required this.imageUrl});
  const Season.spring()
      : name = 'Spring',
        imageUrl = 'https://cnn.it/3xu58Ap';
  const Season.summer()
      : name = 'Summer',
        imageUrl = 'https://bit.ly/2VcCSow';
  const Season.autumn()
      : name = 'Autumn',
        imageUrl = 'https://bit.ly/3A3zStC';
  const Season.winter()
      : name = 'Winter',
        imageUrl = 'https://bit.ly/2TNY7wi';
}

const allSeasons = [
  Season.spring(),
  Season.summer(),
  Season.autumn(),
  Season.winter()
];

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    final width = MediaQuery.of(context).size.width;
    final height = width / (16.0 / 9.0);
    return Scaffold(
      appBar: AppBar(
        title: const Text('PageScrollPhysics in Flutter'),
      ),
      body: SizedBox(
        width: width,
        height: height,
        child: ListView(
          shrinkWrap: true,
          scrollDirection: Axis.horizontal,
          physics: const PageScrollPhysics(),
          clipBehavior: Clip.antiAlias,
          children: allSeasons.map((season) {
            return SizedBox(
              width: width,
              height: height,
              child: Image.network(
                season.imageUrl,
                height: height,
                fit: BoxFit.cover,
              ),
            );
          }).toList(),
        ),
      ),
    );
  }
}

Immutable Classes in Dart



import 'package:flutter/foundation.dart' show immutable;

@immutable
abstract class Animal {
  final String name;
  const Animal(this.name);
}

class Cat extends Animal {
  const Cat() : super('Cindy Clawford');
}

class Dog extends Animal {
  int age;
  Dog()
      : age = 0,
        super('Bark Twain');
}

Card Widget in Flutter



class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Card in Flutter'),
      ),
      body: Image.network(
        'https://bit.ly/36fNNj9',
        frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
          return Card(
            child: child,
            clipBehavior: Clip.antiAlias,
          );
        },
        loadingBuilder: (context, child, loadingProgress) {
          final totalBytes = loadingProgress?.expectedTotalBytes;
          final bytesLoaded = loadingProgress?.cumulativeBytesLoaded;
          if (totalBytes != null && bytesLoaded != null) {
            return Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: const [CircularProgressIndicator()],
            );
          } else {
            return child;
          }
        },
      ),
    );
  }
}

List Equality Ignoring Ordering in Dart



@immutable
class Person {
  final String name;
  const Person(this.name);

  @override
  bool operator ==(covariant Person other) => other.name == name;

  @override
  int get hashCode => name.hashCode;

  @override
  String toString() => name;
}

const people1 = [Person('Foo'), Person('Bar'), Person('Baz')];
const people2 = [Person('Foo'), Person('Bar'), Person('Baz')];
const people3 = [Person('Bar'), Person('Bar'), Person('Baz')];
const people4 = [Person('Bar'), Person('Baz')];

extension IsEqualToIgnoringOrdering<T> on List<T> {
  bool isEqualToIgnoringOrdering(List<T> other) =>
      length == other.length &&
      {...this}.intersection({...other}).length == length;
}

void testIt() {
  assert(people1.isEqualToIgnoringOrdering(people2));
  assert(!people1.isEqualToIgnoringOrdering(people3));
  assert(!people2.isEqualToIgnoringOrdering(people3));
  assert(!people3.isEqualToIgnoringOrdering(people4));
}

Shorten GitHub URLs in Dart

// Want to support my work 🤝? https://buymeacoffee.com/vandad

import 'dart:developer' as devtools show log;
import 'dart:convert' show utf8;

Future<Uri> shortenGitHubUrl(String longUrl) =>
    HttpClient().postUrl(Uri.parse('https://git.io/')).then((req) {
      req.add(utf8.encode('url=$longUrl'));
      return req.close();
    }).then(
      (resp) async {
        try {
          final location = resp.headers[HttpHeaders.locationHeader]?.first;
          if (location != null) {
            return Uri.parse(location);
          } else {
            throw 'No location was specified';
          }
        } catch (e) {
          return Uri.parse(longUrl);
        }
      },
    );

void testIt() async {
  final uri = await shortenGitHubUrl(
      'https://github.com/vandadnp/flutter-tips-and-tricks');
  devtools.log(uri.toString());
  // logs https://git.io/JS5Fm
}

Time Picker in Flutter



class HomePage extends StatelessWidget {
  final timeOfDay = ValueNotifier<TimeOfDay?>(null);
  HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder(
      valueListenable: timeOfDay,
      builder: (context, value, child) {
        final title = timeOfDay.value?.toString() ?? 'Time Picker in Flutter';
        return Scaffold(
          appBar: AppBar(title: Text(title)),
          body: Center(
            child: TextButton(
              onPressed: () async {
                timeOfDay.value = await showTimePicker(
                  context: context,
                  initialTime: TimeOfDay.now(),
                  initialEntryMode: TimePickerEntryMode.input,
                );
              },
              child: const Text('Please Pick a time'),
            ),
          ),
        );
      },
    );
  }
}

Throttled Print in Flutter



Stream<String> getStream() => Stream.periodic(
      const Duration(milliseconds: 100),
      (e) => DateTime.now().toString(),
    );

void testIt() async {
  await for (final now in getStream()) {
    debugPrintThrottled(now);
  }
}

Map Equality in Dart



typedef Name = String;
typedef Age = int;

const Map<Name, Age> people1 = {
  'foo': 20,
  'bar': 30,
  'baz': 40,
};

const Map<Name, Age> people2 = {
  'baz': 40,
  'foo': 20,
  'bar': 30,
};

void testIt() {
  assert(mapEquals(people1, people2));
}

Unique Maps in Dart



import 'dart:developer' as devtools show log;

typedef Name = String;
typedef Age = int;

const Map<Name, Age> people = {
  'foo': 20,
  'bar': 30,
  'baz': 20,
};

extension Unique<K, V> on Map<K, V> {
  Map<K, V> unique() {
    Map<K, V> result = {};
    for (final value in {...values}) {
      final firstKey = keys.firstWhereOrNull((key) => this[key] == value);
      if (firstKey != null) {
        result[firstKey] = value;
      }
    }
    return result;
  }
}

void testIt() {
  final uniques = people.unique();
  devtools.log(uniques.toString());
  // prints: {foo: 20, bar: 30}
}

Raw Auto Complete in Flutter



const emailProviders = [
  'gmail.com',
  'hotmail.com',
  'yahoo.com',
];

const icons = [
  'https://bit.ly/3HsvvvB',
  'https://bit.ly/3n6GW4L',
  'https://bit.ly/3zf2RLy',
];

class EmailTextField extends StatefulWidget {
  const EmailTextField({Key? key}) : super(key: key);

  @override
  State<EmailTextField> createState() => _EmailTextFieldState();
}

class _EmailTextFieldState extends State<EmailTextField> {
  late final TextEditingController _controller;
  late final FocusNode _focus;

  @override
  Widget build(BuildContext context) {
    return RawAutocomplete<String>(
      textEditingController: _controller,
      focusNode: _focus,
      fieldViewBuilder: (_, controller, focusNode, onSubmitted) {
        return TextFormField(
          controller: controller,
          focusNode: focusNode,
          onFieldSubmitted: (value) {
            onSubmitted();
          },
        );
      },
      optionsBuilder: (textEditingValue) {
        final lastChar = textEditingValue.text.characters.last;
        if (lastChar == '@') {
          return emailProviders;
        } else {
          return [];
        }
      },
      optionsViewBuilder: (context, onSelected, options) {
        return OptionsList(
          onSelected: onSelected,
          options: options,
          controller: _controller,
        );
      },
    );
  }

  @override
  void initState() {
    _controller = TextEditingController();
    _focus = FocusNode();
    super.initState();
  }

  @override
  void dispose() {
    _focus.dispose();
    _controller.dispose();
    super.dispose();
  }
}

class OptionsList extends StatelessWidget {
  final Iterable<String> options;
  final AutocompleteOnSelected<String> onSelected;
  final TextEditingController controller;
  const OptionsList({
    Key? key,
    required this.onSelected,
    required this.options,
    required this.controller,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Align(
      alignment: Alignment.topLeft,
      child: Material(
        child: SizedBox(
          height: 150,
          child: ListView.builder(
            padding: const EdgeInsets.all(0.0),
            itemCount: options.length,
            itemBuilder: (context, index) {
              final option = options.elementAt(index);
              return GestureDetector(
                onTap: () => onSelected(controller.text + option),
                child: ListTile(
                  horizontalTitleGap: 2.0,
                  leading: Image.network(
                    icons[index],
                    width: 24,
                    height: 24,
                  ),
                  title: Text(option),
                ),
              );
            },
          ),
        ),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Raw Auto Complete in Flutter'),
      ),
      body: const Padding(
        padding: EdgeInsets.all(16.0),
        child: EmailTextField(),
      ),
    );
  }
}

Title on Object in Dart



import 'dart:developer' as devtools show log;

extension CapTitle on Object {
  String get capitalizedTitle {
    String str;
    if (this is Enum) {
      str = (this as Enum).name;
    } else {
      str = toString();
    }
    return str[0].toUpperCase() + str.substring(1);
  }
}

enum EmailProviders { gmail, yahoo, hotmail }

void testIt() {
  EmailProviders.values.map((p) => p.capitalizedTitle).forEach(devtools.log);
  // prints these:
  // Gmail
  // Yahoo
  // Hotmail
}

Compute in Flutter



import 'dart:developer' as devtools show log;
import 'dart:convert' show utf8, json;

@immutable
class Person {
  final String name;
  final int age;
  const Person(this.name, this.age);
  Person.fromJson(Map<String, dynamic> json)
      : name = json["name"] as String,
        age = json["age"] as int;
}

Future<Iterable<Person>> downloadAndParsePersons(Uri uri) => HttpClient()
    .getUrl(uri)
    .then((req) => req.close())
    .then((response) => response.transform(utf8.decoder).join())
    .then((jsonString) => json.decode(jsonString) as List<dynamic>)
    .then((json) => json.map((map) => Person.fromJson(map)));

void testIt() async {
  final persons = await compute(
    downloadAndParsePersons,
    Uri.parse('https://bit.ly/3Jjcw8R'),
  );
  devtools.log(persons.toString());
}

Filter on Map in Dart



import 'dart:developer' as devtools show log;

typedef Name = String;
typedef Age = int;

extension Filter<K, V> on Map<K, V> {
  Iterable<MapEntry<K, V>> filter(
    bool Function(MapEntry<K, V> entry) f,
  ) sync* {
    for (final entry in entries) {
      if (f(entry)) {
        yield entry;
      }
    }
  }
}

const Map<Name, Age> people = {
  'foo': 20,
  'bar': 31,
  'baz': 25,
  'qux': 32,
};

void testIt() async {
  final peopleOver30 = people.filter((e) => e.value > 30);
  devtools.log(peopleOver30.toString());
  // ☝🏻 prints (MapEntry(bar: 31), MapEntry(qux: 32))
}

Type Alias in Dart



const Map<String, int> people1 = {
  'foo': 20,
  'bar': 30,
  'baz': 25,
};

typedef Age = int;

const Map<String, Age> people2 = {
  'foo': 20,
  'bar': 30,
  'baz': 25,
};

ValueNotifier in Flutter



class DynamicToolTipTextField extends StatelessWidget {
  final TextInputType? keyboardType;
  final ValueNotifier<String?> hint;
  final TextEditingController controller;
  const DynamicToolTipTextField({
    Key? key,
    required this.hint,
    required this.controller,
    this.keyboardType,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder(
      valueListenable: hint,
      builder: (context, value, child) {
        return TextField(
          keyboardType: keyboardType,
          controller: controller,
          decoration: InputDecoration(
            hintText: value as String?,
          ),
        );
      },
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

@immutable
abstract class HasText {
  String get text;
}

enum Hint { pleaseEnterYourEmail, youForgotToEnterYourEmail }

extension GetText on Hint {
  String get text {
    switch (this) {
      case Hint.pleaseEnterYourEmail:
        return 'Please enter your email';
      case Hint.youForgotToEnterYourEmail:
        return 'You forgot to enter your email';
    }
  }
}

class _HomePageState extends State<HomePage> {
  late final ValueNotifier<String?> _hint;
  late final TextEditingController _controller;

  @override
  void initState() {
    _hint = ValueNotifier<String?>(Hint.pleaseEnterYourEmail.text);
    _controller = TextEditingController();
    super.initState();
  }

  @override
  void dispose() {
    _hint.dispose();
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('ValueNotifier in Flutter'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          children: [
            DynamicToolTipTextField(
              hint: _hint,
              controller: _controller,
              keyboardType: TextInputType.emailAddress,
            ),
            TextButton(
              onPressed: () async {
                final email = _controller.text;
                if (email.trim().isEmpty) {
                  _hint.value = Hint.youForgotToEnterYourEmail.text;
                  await Future.delayed(const Duration(seconds: 2));
                  _hint.value = Hint.pleaseEnterYourEmail.text;
                }
              },
              child: const Text('Log in'),
            )
          ],
        ),
      ),
    );
  }
}

Object to Integer in Dart



enum ToIntStrategy { round, floor, ceil }

typedef ToIntOnErrorHandler = int Function(Object e);

extension ToInt on Object {
  int toInteger({
    ToIntStrategy strategy = ToIntStrategy.round,
    ToIntOnErrorHandler? onError,
  }) {
    try {
      final doubleValue = double.parse(toString());
      switch (strategy) {
        case ToIntStrategy.round:
          return doubleValue.round();
        case ToIntStrategy.floor:
          return doubleValue.floor();
        case ToIntStrategy.ceil:
          return doubleValue.ceil();
      }
    } catch (e) {
      if (onError != null) {
        return onError(e);
      } else {
        return -1;
      }
    }
  }
}

void testIt() {
  assert('xyz'.toInteger(onError: (_) => 100) == 100);
  assert(1.5.toInteger() == 2);
  assert(1.6.toInteger() == 2);
  assert('1.2'.toInteger(strategy: ToIntStrategy.floor) == 1);
  assert('1.2'.toInteger(strategy: ToIntStrategy.ceil) == 2);
  assert('1.5'.toInteger(strategy: ToIntStrategy.round) == 2);
}

Image Opacity in Flutter



class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller;
  late final Animation<double> _opacity;

  @override
  void initState() {
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 1),
    );
    _opacity = Tween(begin: 0.0, end: 1.0).animate(_controller);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Image.network(
            'https://bit.ly/3ywI8l6',
            opacity: _opacity,
          ),
          Slider(
            value: _controller.value,
            onChanged: (value) {
              setState(() => _controller.value = value);
            },
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

Covariant in Dart

// Want to support my work 🤝? https://buymeacoffee.com/vandad

class Person {
  final String name;
  const Person(this.name);
  @override
  bool operator ==(Object other) {
    if (other is! Person) throw ArgumentError('Was expecting a person');
    return other.name == name;
  }

  @override
  int get hashCode => name.hashCode;
}

class Person {
  final String name;
  const Person(this.name);
  @override
  bool operator ==(covariant Person other) => other.name == name;

  @override
  int get hashCode => name.hashCode;
}

Custom Errors in Streams in Dart



class Either<V, E extends Exception> {
  final V? value;
  final E? error;

  const Either({this.value, this.error}) : assert((value ?? error) != null);

  bool get isError => error != null;
  bool get isValue => value != null;

  @override
  String toString() {
    if (value != null) {
      return "Value: $value";
    } else if (error != null) {
      return "Error: $error";
    } else {
      return 'Unknown state';
    }
  }
}

class DateTimeException implements Exception {
  final String reason;
  const DateTimeException({required this.reason});
}

Stream<Either<DateTime, DateTimeException>> getDateTime() async* {
  var index = 0;
  while (true) {
    if (index % 2 == 0) {
      yield Either(value: DateTime.now());
    } else {
      yield const Either(
        error: DateTimeException(reason: 'Something is wrong!'),
      );
    }
    index += 1;
  }
}

void testIt() async {
  await for (final value in getDateTime()) {
    dev.log(value.toString());
  }
}

Shake Animation in Flutter



class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

const animationWidth = 10.0;

class _HomePageState extends State<HomePage>
    with SingleTickerProviderStateMixin {
  late final TextEditingController _textController;
  late final AnimationController _animationController;
  late final Animation<double> _offsetAnim;
  final defaultHintText = 'Please enter your email here 😊';
  var _hintText = '';

  @override
  void initState() {
    _hintText = defaultHintText;
    _textController = TextEditingController();
    _animationController = AnimationController(
      duration: const Duration(milliseconds: 370),
      vsync: this,
    );
    _offsetAnim = Tween(
      begin: 0.0,
      end: animationWidth,
    ).chain(CurveTween(curve: Curves.elasticIn)).animate(
          _animationController,
        )..addStatusListener(
        (status) {
          if (status == AnimationStatus.completed) {
            _animationController.reverse();
          }
        },
      );
    super.initState();
  }

  @override
  void dispose() {
    _textController.dispose();
    _animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Shake Animation in Flutter'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          children: [
            AnimatedBuilder(
              animation: _offsetAnim,
              builder: (context, child) {
                return Container(
                  margin: const EdgeInsets.symmetric(
                    horizontal: animationWidth,
                  ),
                  padding: EdgeInsets.only(
                    left: _offsetAnim.value + animationWidth,
                    right: animationWidth - _offsetAnim.value,
                  ),
                  child: TextField(
                    controller: _textController,
                    keyboardType: TextInputType.emailAddress,
                    decoration: InputDecoration(
                      hintText: _hintText,
                    ),
                  ),
                );
              },
            ),
            TextButton(
                onPressed: () async {
                  if (_textController.text.isEmpty) {
                    setState(() {
                      _hintText = 'You forgot to enter your email 🥲';
                      _animationController.forward(from: 0.0);
                    });
                    await Future.delayed(const Duration(seconds: 3));
                    setState(() {
                      _hintText = defaultHintText;
                    });
                  }
                },
                child: const Text('Login'))
          ],
        ),
      ),
    );
  }
}

Throw Enums in Dart



import 'dart:developer' as dev show log;

enum Exceptions { invalidUserName, invalidPassword }

void thisMethodThrows() {
  throw Exceptions.invalidPassword;
}

void testIt() {
  try {
    thisMethodThrows();
  } on Exceptions catch (e) {
    switch (e) {
      case (Exceptions.invalidUserName):
        dev.log("Invalid user name");
        break;
      case (Exceptions.invalidPassword):
        dev.log("Invalid password");
        break;
    }
  }
}

Future Error Test in Flutter



import 'dart:developer' as dev show log;

@immutable
abstract class UserException implements Exception {}

class InvalidUserNameException extends UserException {}

class InvalidUserAgeException extends UserException {}

@immutable
class User {
  final String name;
  final int age;
  User({required this.name, required this.age}) {
    if (!name.contains(RegExp(r'^[a-z ]+$'))) {
      throw InvalidUserNameException();
    } else if (age < 0 || age > 130) {
      throw InvalidUserAgeException();
    }
  }
  const User.anonymous()
      : name = 'Anonymous User',
        age = 0;
}

Future<User> getAsyncUser() => Future.delayed(
      const Duration(seconds: 1),
      () => User(name: 'Foo', age: 20),
    );

void testIt() async {
  final user = await getAsyncUser()
      .catchError(
        handleInvalidUsernameException,
        test: (e) => e is InvalidUserNameException,
      )
      .catchError(
        handleInvalidAgeException,
        test: (e) => e is InvalidUserAgeException,
      );
  dev.log(user.toString());
}

User handleInvalidUsernameException(Object? e) {
  dev.log(e.toString());
  return const User.anonymous();
}

User handleInvalidAgeException(Object? e) {
  dev.log(e.toString());
  return const User.anonymous();
}

Generic URL Retrieval in Dart



import 'dart:developer' as dev show log;

typedef StatusCodeResultBuilder<T> = Future<T> Function(
  int statusCode,
  HttpClientResponse response,
);

extension Get on Uri {
  Future<T?> getBody<T>({
    StatusCodeResultBuilder<T>? statusBuilder,
    T Function(Object error)? onNetworkError,
  }) async {
    try {
      final apiCall = await HttpClient().getUrl(this);
      final response = await apiCall.close();
      final builder = statusBuilder;
      if (builder == null) {
        final data = await response.transform(convert.utf8.decoder).join();
        if (data is T) {
          return data as T?;
        } else {
          return null;
        }
      } else {
        final result = await builder(response.statusCode, response);
        return result;
      }
    } catch (e) {
      if (onNetworkError != null) {
        return onNetworkError(e);
      } else {
        return null;
      }
    }
  }
}

extension ToUri on String {
  Uri toUri() => Uri.parse(this);
}

const url = 'https://bit.ly/3EKWcLa';

void testIt() async {
  final json = await url.toUri().getBody<String>(
    statusBuilder: (statusCode, response) async {
      if (statusCode == 200) {
        return await response.transform(convert.utf8.decoder).join();
      } else {
        return "{'error': 'Unexpected status code $statusCode'}";
      }
    },
    onNetworkError: (error) {
      return "{'error': 'Got network error'}";
    },
  );
  if (json != null) {
    dev.log(json);
  }
}

Custom Error Widget in Flutter



class MyErrorWidget extends StatelessWidget {
  final String text;
  const MyErrorWidget({Key? key, required this.text}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SizedBox(
        width: MediaQuery.of(context).size.width,
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              Image.network('https://bit.ly/3gHlTCU'),
              Text(
                text,
                textAlign: TextAlign.center,
                style: const TextStyle(
                  color: Colors.red,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

void main() {
  ErrorWidget.builder = (FlutterErrorDetails details) {
    bool isInDebugMode = false;
    assert(() {
      isInDebugMode = true;
      return true;
    }());
    final message = details.exception.toString();
    if (isInDebugMode) {
      return MyErrorWidget(text: message);
    } else {
      return Text(
        message,
        textAlign: TextAlign.center,
      );
    }
  };

  runApp(
    const MaterialApp(
      home: HomePage(),
      debugShowCheckedModeBanner: false,
    ),
  );
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Error Widget in Flutter'),
      ),
      body: Builder(
        builder: (context) {
          throw Exception(
              'Here is an exception that is caught by our custom Error Widget in Flutter');
        },
      ),
    );
  }
}

Handle Multiple Future Errors in Dart



import 'dart:developer' as dev show log;

Future<Iterable<T>> waitOn<T>(
  Iterable<Future<T>> futures,
  Function onError,
) async {
  List<T> result = [];
  for (final future in futures) {
    final value = await future.catchError(onError);
    result.add(value);
  }
  return result;
}

void testIt() async {
  final f1 = Future.error('First Error');
  final f2 = Future.delayed(const Duration(seconds: 2), () => 10);
  final f3 = Future.error('Second error');
  final f4 = Future.delayed(const Duration(seconds: 2), () => 'Hello world');
  final result = await waitOn([f1, f2, f3, f4], (error) => -1);
  dev.log(result.toString()); // [-1, 10, -1, Hello world]
}

Future Error Handling in Dart

import 'dart:developer' as dev show log;

extension OnError<T> on Future<T> {
  Future<T> onErrorJustReturn(T value) => catchError((_) => value);
}

Future<bool> isUserRegistered({required String email}) => HttpClient()
    .postUrl(Uri.parse('https://website'))
    .then((req) {
      req.headers.add('email', email);
      return req.close();
    })
    .then((resp) => resp.statusCode == 200)
    .onErrorJustReturn(false);

void testIt() async {
  final isFooRegistered = await isUserRegistered(email: 'foo@flutter.com');
  dev.log(isFooRegistered.toString());
}

String to Toast in Flutter



extension Toast on String {
  Future<void> showAsToast(BuildContext context,
      {required Duration duration}) async {
    final scaffold = ScaffoldMessenger.of(context);
    final controller = scaffold.showSnackBar(
      SnackBar(
        content: Text(this),
        backgroundColor: const Color(0xFF24283b),
        behavior: SnackBarBehavior.floating,
        elevation: 2.0,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(10),
        ),
      ),
    );
    await Future.delayed(duration);
    controller.close();
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: TextButton(
          onPressed: () => 'Hello, World!'.showAsToast(
            context,
            duration: const Duration(seconds: 2),
          ),
          child: const Text('Show the snackbar'),
        ),
      ),
    );
  }
}

Waiting in Dart



Future<void> wait(Duration d) async {
  await Future.delayed(d);
}

extension Wait on int {
  Future<void> get seconds => wait(Duration(seconds: this));
  Future<void> get minutes => wait(Duration(minutes: this));
}

void testIt() async {
  await 2.seconds;
  'After 2 seconds'.log();
  await 3.minutes;
  'After 3 minutes'.log();
}

extension Log on Object {
  void log() {
    dev.log(toString());
  }
}

Loading Dialog in Flutter



typedef CloseDialog = void Function();

CloseDialog showLoadingScreen({
  required BuildContext context,
  required String text,
}) {
  final dialog = AlertDialog(
    content: Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        const CircularProgressIndicator(),
        const SizedBox(height: 10),
        Text(text),
      ],
    ),
  );
  showDialog(
    context: context,
    barrierDismissible: false,
    builder: (_) => dialog,
  );

  return () => Navigator.of(context).pop();
}

void testIt(BuildContext context) async {
  final closeDialog = showLoadingScreen(
    context: context,
    text: 'Loading data...',
  );
  await Future.delayed(const Duration(seconds: 2));
  closeDialog();
}

Compact Map on Map<K,V> in Dart



const foo = 'foo';
const bar = 'bar';
const baz = 'baz';

const namesAndAges = {
  foo: 20,
  bar: 25,
  baz: 18,
};

const acceptedNames = [
  foo,
  bar,
];

void testIt() {
  final acceptedAges = namesAndAges.compactMap(
    (e) => acceptedNames.contains(e.key) ? e.value : null,
  );
  acceptedAges.log(); // [20, 25]
}

extension CompactMap<T, E> on Map<T, E> {
  Iterable<V> compactMap<V>(V? Function(MapEntry<T, E>) f) sync* {
    for (final entry in entries) {
      final extracted = f(entry);
      if (extracted != null) {
        yield extracted;
      }
    }
  }
}

Query Parameters in Dart



import 'dart:developer' as devtools show log;

const host = 'freecurrencyapi.net';
const path = '/api/v2/latest';
const apiKey = 'YOUR_API_KEY';
const baseCurrency = 'sek';
const params = {
  'apiKey': apiKey,
  'base_currency': 'sek',
};

void insteadOfThis() {
  const url = 'https://$host$path?apiKey=$apiKey&base_currency=$baseCurrency';
  url.log();
}

void doThis() {
  final url = Uri.https(host, path, params);
  url.log();
}

extension Log on Object {
  void log() {
    devtools.log(toString());
  }
}

Multiple Gradients in Container in Flutter



typedef GradientContainersBuilder = Map<LinearGradient, Widget?> Function();

class GradientContainers extends StatelessWidget {
  final GradientContainersBuilder builder;

  const GradientContainers({
    Key? key,
    required this.builder,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: builder().entries.map((mapEntry) {
        final gradient = mapEntry.key;
        final widget = mapEntry.value;
        return GradientContainer(
          gradient: gradient,
          child: widget,
        );
      }).toList(),
    );
  }
}

class GradientContainer extends StatelessWidget {
  final LinearGradient gradient;
  final Widget? child;

  const GradientContainer({Key? key, required this.gradient, this.child})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Positioned.fill(
      child: Container(
        decoration: BoxDecoration(
          gradient: gradient,
        ),
        child: child,
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GradientContainers(
        builder: () => {
          topLeftToBottomRightGradient: null,
          rightToLeftGradient: null,
          leftToRightGradinet: null,
          bottomRightGradient: Image.network('https://bit.ly/3otHHog'),
        },
      ),
    );
  }
}

const transparent = Color(0x00FFFFFF);

const topLeftToBottomRightGradient = LinearGradient(
  begin: Alignment.topLeft,
  end: Alignment.bottomRight,
  colors: [
    Color(0xff2ac3de),
    transparent,
  ],
);

const bottomRightGradient = LinearGradient(
  begin: Alignment.bottomRight,
  end: Alignment.topLeft,
  colors: [
    Color(0xffbb9af7),
    transparent,
  ],
);

const rightToLeftGradient = LinearGradient(
  begin: Alignment.centerRight,
  end: Alignment.centerLeft,
  colors: [
    Color(0xff9ece6a),
    transparent,
  ],
);

const leftToRightGradinet = LinearGradient(
  begin: Alignment.centerLeft,
  end: Alignment.centerRight,
  colors: [
    Color(0xff7dcfff),
    transparent,
  ],
);

void main() {
  runApp(
    const MaterialApp(
      home: HomePage(),
      debugShowCheckedModeBanner: false,
    ),
  );
}

Filter on Stream<List<T>> in Dart



import 'dart:developer' as devtools show log;

extension Filter<T> on Stream<List<T>> {
  Stream<List<T>> filter(bool Function(T) where) =>
      map((items) => items.where(where).toList());
}

final Stream<List<int>> allNumbers = Stream.periodic(
  const Duration(seconds: 1),
  (value) => [for (var i = 0; i < value; i++) i],
);

bool isEven(num value) => value % 2 == 0;
bool isOdd(num value) => value % 2 != 0;

extension EvenOdd<E extends num> on Stream<List<E>> {
  Stream<List<E>> get evenNumbers => filter(isEven);
  Stream<List<E>> get oddNumbers => filter(isOdd);
}

void readEvenNumbers() async {
  await for (final evenNumber in allNumbers.evenNumbers) {
    devtools.log('All even numbers: $evenNumber');
  }
}

void readOddNumbers() async {
  await for (final oddNumber in allNumbers.oddNumbers) {
    devtools.log('All odd numbers: $oddNumber');
  }
}

Generic Route Arguments in Flutter

extension GetArgument on BuildContext {
  T? getArgument<T>() {
    final modalRoute = ModalRoute.of(this);
    if (modalRoute != null) {
      final args = modalRoute.settings.arguments;
      if (args != null && args is T) {
        return args as T;
      }
    }
    return null;
  }
}

Generic Dialog in Flutter

typedef DialogOptionBuilder<T> = Map<String, T> Function();

Future<T?> showGenericDialog<T>({
  required BuildContext context,
  required String title,
  required String content,
  required DialogOptionBuilder optionsBuilder,
}) {
  final options = optionsBuilder();
  return showDialog<T>(
    context: context,
    builder: (context) {
      return AlertDialog(
        title: Text(title),
        content: Text(content),
        actions: options.keys.map(
          (optionTitle) {
            final T value = options[optionTitle];
            return TextButton(
              onPressed: () {
                Navigator.of(context).pop(value);
              },
              child: Text(optionTitle),
            );
          },
        ).toList(),
      );
    },
  );
}

Future<bool> showLogOutDialog(BuildContext context) {
  return showGenericDialog<bool>(
    context: context,
    title: 'Log out',
    content: 'Are you sure you want to log out?',
    optionsBuilder: () => {
      'Cancel': false,
      'Log out': true,
    },
  ).then(
    (value) => value ?? false,
  );
}

GitHub API in Flutter



import 'dart:io' show HttpHeaders, HttpClient;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:convert' show utf8, json;

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

Future<Iterable<GithubUser>> getGithubFollowers(String accessToken) =>
    HttpClient()
        .getUrl(Uri.parse('https://api.github.com/user/followers'))
        .then((req) {
          req.headers
            ..set(HttpHeaders.authorizationHeader, 'Bearer $accessToken')
            ..set(HttpHeaders.contentTypeHeader, 'application/json');
          return req.close();
        })
        .then((resp) => resp.transform(utf8.decoder).join())
        .then((jsonStr) => json.decode(jsonStr) as List<dynamic>)
        .then(
          (jsonArray) => jsonArray.compactMap((element) {
            if (element is Map<String, dynamic>) {
              return element;
            } else {
              return null;
            }
          }),
        )
        .then(
          (listOfMaps) => listOfMaps.map(
            (map) => GithubUser.fromJson(map),
          ),
        );

class GithubUser {
  final String username;
  final String avatarUrl;

  GithubUser.fromJson(Map<String, dynamic> json)
      : username = json['login'] as String,
        avatarUrl = json['avatar_url'] as String;
}

extension CompactMap<T> on List<T> {
  List<E> compactMap<E>(E? Function(T element) f) {
    Iterable<E> imp(E? Function(T element) f) sync* {
      for (final value in this) {
        final mapped = f(value);
        if (mapped != null) {
          yield mapped;
        }
      }
    }

    return imp(f).toList();
  }
}

const token = 'PUT_YOUR_TOKEN_HERE';

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('GitHub API in Flutter'),
      ),
      body: FutureBuilder(
        future: getGithubFollowers(token),
        builder: (context, snapshot) {
          switch (snapshot.connectionState) {
            case ConnectionState.done:
              final users = (snapshot.data as Iterable<GithubUser>).toList();
              return ListView.builder(
                itemCount: users.length,
                itemBuilder: (context, index) {
                  final user = users[index];
                  return ListTile(
                    title: Text(user.username),
                    leading: CircularAvatar(url: user.avatarUrl),
                  );
                },
              );
            default:
              return const CircularProgressIndicator();
          }
        },
      ),
    );
  }
}

void main() {
  runApp(
    const MaterialApp(
      home: HomePage(),
      debugShowCheckedModeBanner: false,
    ),
  );
}

ChangeNotifier in Flutter



import 'package:provider/provider.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

const allImages = [
  'https://bit.ly/3ywI8l6',
  'https://bit.ly/36fNNj9',
  'https://bit.ly/3jOueGG',
  'https://bit.ly/3qYOtDm',
  'https://bit.ly/3wt11Ec',
  'https://bit.ly/3yvFg7X',
  'https://bit.ly/3ywzOla',
  'https://bit.ly/3wnASpW',
  'https://bit.ly/3jXSDto',
];

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class ImageData {
  final Uint8List imageData;
  const ImageData(this.imageData);
}

class Images extends ChangeNotifier {
  final List<ImageData> _items = [];

  var _isLoading = false;
  bool get isLoading => _isLoading;

  UnmodifiableListView<ImageData> get items => UnmodifiableListView(_items);

  void loadNextImage() async {
    if (_items.length < allImages.length) {
      // time to load more
      _isLoading = true;
      notifyListeners();
      final imageUrl = allImages[_items.length];
      final networkAsset = NetworkAssetBundle(Uri.parse(imageUrl));
      final loaded = await networkAsset.load(imageUrl);
      final bytes = loaded.buffer.asUint8List();
      final imageData = ImageData(bytes);
      _items.insert(0, imageData);
      _isLoading = false;
      notifyListeners();
    } else {
      if (isLoading) {
        _isLoading = false;
        notifyListeners();
      }
    }
  }
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('ChangeNotifier in Flutter'),
        actions: [
          Consumer<Images>(
            builder: (context, value, child) {
              return IconButton(
                onPressed: () {
                  value.loadNextImage();
                },
                icon: const Icon(Icons.add_box_outlined),
              );
            },
          )
        ],
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Consumer<Images>(
          builder: (context, value, child) {
            final images = value.items;
            final isLoading = value.isLoading;
            return ListView.builder(
              itemBuilder: (context, index) {
                if (index == 0 && isLoading) {
                  return Center(
                    child: Column(
                      children: const [
                        CircularProgressIndicator(),
                        SizedBox(height: 16.0),
                      ],
                    ),
                  );
                } else {
                  final imageIndex = isLoading ? index - 1 : index;
                  final imageData = images[imageIndex].imageData;
                  return Column(
                    children: [
                      RoundedImageWithShadow(imageData: imageData),
                      const SizedBox(height: 16.0),
                    ],
                  );
                }
              },
              itemCount: isLoading ? images.length + 1 : images.length,
            );
          },
        ),
      ),
    );
  }
}

class RoundedImageWithShadow extends StatelessWidget {
  final Uint8List imageData;
  const RoundedImageWithShadow({Key? key, required this.imageData})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      clipBehavior: Clip.antiAlias,
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(20),
        boxShadow: [
          BoxShadow(
            blurRadius: 2,
            color: Colors.black.withAlpha(40),
            spreadRadius: 2,
          ),
        ],
      ),
      child: Image.memory(
        imageData,
        fit: BoxFit.cover,
      ),
    );
  }
}

void main() {
  runApp(
    MaterialApp(
      home: ChangeNotifierProvider(
        create: (_) => Images(),
        child: const HomePage(),
      ),
      debugShowCheckedModeBanner: false,
    ),
  );
}

Refresh Indicator in Flutter



import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

const allImages = [
  'https://bit.ly/3x7J5Qt',
  'https://bit.ly/3ywI8l6',
  'https://bit.ly/36fNNj9',
  'https://bit.ly/3jOueGG',
  'https://bit.ly/3qYOtDm',
  'https://bit.ly/3wt11Ec',
  'https://bit.ly/3yvFg7X',
  'https://bit.ly/3ywzOla',
  'https://bit.ly/3wnASpW',
  'https://bit.ly/3jXSDto',
];

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final _images = [allImages.first];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Refresh Indicator in Flutter'),
      ),
      body: RefreshIndicator(
        onRefresh: () async {
          final nextIndex = _images.length + 1;
          if (nextIndex < allImages.length) {
            setState(() {
              _images.insert(0, allImages[nextIndex]);
            });
          }
        },
        child: ListView.builder(
          physics: const AlwaysScrollableScrollPhysics(),
          padding: const EdgeInsets.all(16),
          itemCount: _images.length,
          itemBuilder: (context, index) {
            final imageUrl = _images[index];
            return Column(
              children: [
                RoundedImageWithShadow(url: imageUrl),
                const SizedBox(height: 16),
              ],
            );
          },
        ),
      ),
    );
  }
}

class RoundedImageWithShadow extends StatelessWidget {
  final String url;
  const RoundedImageWithShadow({Key? key, required this.url}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      clipBehavior: Clip.antiAlias,
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(20),
        boxShadow: [
          BoxShadow(
            blurRadius: 2,
            color: Colors.black.withAlpha(40),
            spreadRadius: 2,
          ),
        ],
      ),
      child: Image.network(url),
    );
  }
}

FlatMap in Dart



extension FlatMap<T> on T? {
  E? flatMap<E>(E? Function(T) f) => this != null ? f(this!) : null;
}

AuthUser? get insteadOfThis {
  final user = FirebaseAuth.instance.currentUser;
  if (user != null) {
    return AuthUser.fromFirebase(user);
  } else {
    return null;
  }
}

AuthUser? get doThis =>
    FirebaseAuth.instance.currentUser.flatMap((u) => AuthUser.fromFirebase(u));

OrientationBuilder in Flutter



import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

class RoundedImageWithShadow extends StatelessWidget {
  final String url;
  const RoundedImageWithShadow({Key? key, required this.url}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      clipBehavior: Clip.antiAlias,
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(20),
        boxShadow: [
          BoxShadow(
            blurRadius: 2,
            color: Colors.black.withAlpha(40),
            spreadRadius: 2,
          ),
        ],
      ),
      child: Image.network(url),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: OrientationBuilder(
          builder: (context, orientation) {
            final int count;
            switch (orientation) {
              case Orientation.portrait:
                count = 2;
                break;
              case Orientation.landscape:
                count = 4;
                break;
            }
            return GridView.count(
              padding: const EdgeInsets.all(8.0),
              crossAxisCount: count,
              mainAxisSpacing: 8.0,
              crossAxisSpacing: 8.0,
              children: images
                  .map((url) => RoundedImageWithShadow(url: url))
                  .toList(),
            );
          },
        ),
      ),
    );
  }
}

final images = [
  'https://bit.ly/3qJ2FCf',
  'https://bit.ly/3Hs9JsV',
  'https://bit.ly/3cfT6Cv',
  'https://bit.ly/30wGnIE',
  'https://bit.ly/3kJYsum',
  'https://bit.ly/3oDoMaJ',
  'https://bit.ly/3FndXQM',
  'https://bit.ly/3ci4i1f',
];

Linear Gradient in Flutter



import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Linear Gradient in Flutter'),
      ),
      body: const ImageWithShadow(url: 'https://bit.ly/3otHHog'),
    );
  }
}

class ImageWithShadow extends StatelessWidget {
  final String url;

  const ImageWithShadow({
    Key? key,
    required this.url,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Stack(
        children: [
          Positioned.fill(
            child: Container(
              decoration: BoxDecoration(
                boxShadow: [
                  BoxShadow(
                    blurRadius: 10.0,
                    color: Colors.black.withOpacity(0.5),
                    offset: const Offset(0.0, 3.0),
                  )
                ],
                borderRadius: const BorderRadius.all(Radius.circular(20)),
                gradient: const LinearGradient(
                  begin: Alignment.topCenter,
                  end: Alignment.bottomCenter,
                  colors: [
                    Color.fromARGB(255, 176, 229, 251),
                    Color.fromARGB(255, 235, 202, 250)
                  ],
                ),
              ),
            ),
          ),
          Image.network(url),
        ],
      ),
    );
  }
}

Bloc Text Editing Controller in Flutter

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

abstract class Event {
  const Event();
}

class SearchEvent extends Event {
  final String searchString;
  const SearchEvent(this.searchString);
}

class ClearSearch extends Event {}

class SearchBloc extends Bloc<Event, List<String>> {
  static const names = ['foo', 'bar', 'baz'];

  SearchBloc() : super(names) {
    on<Event>((event, emit) {
      if (event is SearchEvent) {
        emit(names
            .where((element) => element.contains(event.searchString))
            .toList());
      } else if (event is ClearSearch) {
        emit(names);
      }
    });
  }
}

class BlocTextEditingController extends TextEditingController {
  SearchBloc? bloc;
  BlocTextEditingController() {
    addListener(() {
      if (text.isEmpty) {
        bloc?.add(ClearSearch());
      } else {
        bloc?.add(SearchEvent(text));
      }
    });
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

const largeStyle = TextStyle(fontSize: 30);

class _HomePageState extends State<HomePage> {
  late final BlocTextEditingController _controller;

  @override
  void initState() {
    _controller = BlocTextEditingController();
    super.initState();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    _controller.bloc = BlocProvider.of<SearchBloc>(context);
    return Scaffold(
      appBar: AppBar(
        title: Text('Bloc Search in Flutter'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: BlocBuilder<SearchBloc, List<String>>(
          builder: (context, state) {
            return ListView.builder(
              itemBuilder: (context, index) {
                if (index == 0) {
                  // search field
                  return TextField(
                    decoration: InputDecoration(
                      hintText: 'Enter search term here...',
                      hintStyle: largeStyle,
                    ),
                    style: largeStyle,
                    controller: _controller,
                  );
                } else {
                  final name = state[index - 1];
                  return ListTile(
                    title: Text(
                      name,
                      style: largeStyle,
                    ),
                  );
                }
              },
              itemCount: state.length + 1, // +1 for search
            );
          },
        ),
      ),
    );
  }
}

Blurred TabBar in Flutter



import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

const images = [
  'https://bit.ly/3x7J5Qt',
  'https://bit.ly/3ywI8l6',
  'https://bit.ly/36fNNj9',
  'https://bit.ly/3jOueGG',
  'https://bit.ly/3qYOtDm',
  'https://bit.ly/3wt11Ec',
  'https://bit.ly/3yvFg7X',
  'https://bit.ly/3ywzOla',
  'https://bit.ly/3wnASpW',
  'https://bit.ly/3jXSDto',
];

class CustomTabBar extends StatelessWidget {
  final List<IconButton> buttons;

  const CustomTabBar({Key? key, required this.buttons}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Align(
      alignment: Alignment.bottomCenter,
      child: ClipRect(
        child: Container(
          height: 80,
          color: Colors.white.withOpacity(0.4),
          child: BackdropFilter(
            filter: ImageFilter.blur(sigmaX: 4.0, sigmaY: 4.0),
            child: Container(
              width: double.infinity,
              decoration: BoxDecoration(
                color: Colors.white.withOpacity(0.5),
              ),
              child: Padding(
                padding: const EdgeInsets.only(bottom: 15),
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: buttons,
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

const summerIcon = Icon(
  Icons.surfing,
  size: 40.0,
  color: Colors.teal,
);

const autumnIcon = Icon(
  Icons.nature_outlined,
  size: 40.0,
  color: Colors.black45,
);

const winterIcon = Icon(
  Icons.snowboarding,
  size: 40.0,
  color: Colors.black45,
);

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Blurred Tab Bar'),
      ),
      body: Stack(
        children: [
          ListView.builder(
            itemCount: images.length,
            itemBuilder: (context, index) {
              final url = images[index];
              return Image.network(url);
            },
          ),
          CustomTabBar(
            buttons: [
              IconButton(
                icon: summerIcon,
                onPressed: () {
                  // implement me
                },
              ),
              IconButton(
                icon: autumnIcon,
                onPressed: () {
                  // implement me
                },
              ),
              IconButton(
                icon: winterIcon,
                onPressed: () {
                  // implement me
                },
              )
            ],
          )
        ],
      ),
    );
  }
}

Play YouTube in Flutter



import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

const videoIds = [
  'BHACKCNDMW8',
  '26h9hBZFl7w',
  'glENND73k4Q',
  'd0tU18Ybcvk',
];

class VideoView extends StatelessWidget {
  final String videoId;
  final _key = UniqueKey();

  VideoView({required this.videoId});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Watch a Video'),
      ),
      body: Center(
        child: Container(
          height: 232.0,
          child: WebView(
            key: _key,
            initialUrl: 'https://www.youtube.com/embed/$videoId',
            javascriptMode: JavascriptMode.unrestricted,
          ),
        ),
      ),
    );
  }
}

class YouTubeVideoThumbnail extends StatelessWidget {
  final String videoId;
  final String thumbnailUrl;

  const YouTubeVideoThumbnail({Key? key, required this.videoId})
      : thumbnailUrl = 'https://img.youtube.com/vi/$videoId/maxresdefault.jpg',
        super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        Navigator.of(context).push(
          MaterialPageRoute(
            builder: (_) => VideoView(videoId: videoId),
          ),
        );
      },
      child: Container(
        height: 256.0,
        clipBehavior: Clip.antiAlias,
        decoration: BoxDecoration(
          boxShadow: [
            BoxShadow(
              blurRadius: 10.0,
              color: Colors.black.withAlpha(50),
              spreadRadius: 10.0,
            ),
          ],
          borderRadius: BorderRadius.circular(20),
          image: DecorationImage(
            fit: BoxFit.fitHeight,
            image: NetworkImage(thumbnailUrl),
          ),
        ),
        child: Center(
          child: Icon(
            Icons.play_arrow,
            color: Colors.white,
            size: 100.0,
          ),
        ),
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('YouTube Videos in Flutter')),
      body: ListView.builder(
        itemCount: videoIds.length,
        itemBuilder: (context, index) {
          final videoId = videoIds[index];
          return Padding(
            padding: const EdgeInsets.all(8.0),
            child: YouTubeVideoThumbnail(videoId: videoId),
          );
        },
      ),
    );
  }
}

ListView Background in Flutter



import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

class ListItem {
  const ListItem();
  factory ListItem.emptyTile() => EmptyTile();
  factory ListItem.tile(
    String title,
    String subTitle,
  ) =>
      Tile(
        title,
        subTitle,
      );
}

class Tile extends ListItem {
  final String title;
  final String subTitle;
  const Tile(this.title, this.subTitle) : super();
}

class EmptyTile extends ListItem {}

final items = [
  for (var i = 1; i <= 6; i++) ListItem.tile('Title $i', 'Sub title $i'),
  ListItem.emptyTile(),
  for (var i = 7; i <= 12; i++) ListItem.tile('Title $i', 'Sub title $i'),
];

class Background extends StatelessWidget {
  final Widget child;
  const Background({Key? key, required this.child}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Container(
      clipBehavior: Clip.antiAlias,
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(20),
        image: DecorationImage(
          fit: BoxFit.fitHeight,
          image: NetworkImage('https://bit.ly/3jXSDto'),
        ),
      ),
      child: child,
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Background(
        child: ListView.builder(
          padding: EdgeInsets.zero,
          itemCount: items.length,
          itemBuilder: (context, index) {
            final item = items[index];
            if (item is Tile) {
              return Container(
                color: Colors.grey[200],
                child: ListTile(
                  title: Text(item.title),
                  subtitle: Text(item.subTitle),
                ),
              );
            } else if (item is EmptyTile) {
              return SizedBox(
                height: 450,
              );
            } else {
              throw 'unexpcted item';
            }
          },
        ),
      ),
    );
  }
}

Integer to Binary in Dart

extension ToBinary on int {
  String toBinary(
    int len, {
    int separateAtLength = 4,
    String separator = ',',
  }) =>
      toRadixString(2)
          .padLeft(len, '0')
          .splitByLength(separateAtLength)
          .join(separator);
}

void testIt() {
  assert(1.toBinary(8) == '0000,0001');
  assert(2.toBinary(4) == '0010');
  assert(3.toBinary(16) == '0000,0000,0000,0011');
  assert(255.toBinary(8, separateAtLength: 8) == '11111111');
  assert(255.toBinary(8, separateAtLength: 4) == '1111,1111');
}

extension SplitByLength on String {
  Iterable<String> splitByLength(int len, {String filler = '0'}) sync* {
    final missingFromLength =
        length % len == 0 ? 0 : len - (characters.length % len);
    final expectedLength = length + missingFromLength;
    final src = padLeft(expectedLength, filler);
    final chars = src.characters;
    for (var i = 0; i < chars.length; i += len) {
      yield chars.getRange(i, i + len).toString();
    }
  }
}

Split String by Length in Dart



void testIt() {
  assert('dartlang'
      .splitByLength(5, filler: '💙')
      .isEqualTo(['💙💙dar', 'tlang']));

  assert('0100010'.splitByLength(4).isEqualTo(['0010', '0010']));
  assert('foobar'.splitByLength(3).isEqualTo(['foo', 'bar']));
  assert('flutter'.splitByLength(4, filler: 'X').isEqualTo(['Xflu', 'tter']));
  assert('dart'.splitByLength(5, filler: '').isEqualTo(['dart']));
}

extension SplitByLength on String {
  Iterable<String> splitByLength(int len, {String filler = '0'}) sync* {
    final missingFromLength =
        length % len == 0 ? 0 : len - (characters.length % len);
    final expectedLength = length + missingFromLength;
    final src = padLeft(expectedLength, filler);
    final chars = src.characters;
    for (var i = 0; i < chars.length; i += len) {
      yield chars.getRange(i, i + len).toString();
    }
  }
}

Image Tint in Flutter



import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

enum OverlayColor { brown, orange, yellow, green, blue }

extension Color on OverlayColor {
  MaterialColor get color {
    switch (this) {
      case OverlayColor.blue:
        return Colors.blue;
      case OverlayColor.brown:
        return Colors.brown;
      case OverlayColor.green:
        return Colors.green;
      case OverlayColor.orange:
        return Colors.orange;
      case OverlayColor.yellow:
        return Colors.yellow;
    }
  }
}

extension Title on OverlayColor {
  String get title => toString().split('.').last;
}

extension ToTextButtonWithValue on OverlayColor {
  TextButtonWithValue<OverlayColor> toTextButtonWithValue(
      OnTextButtonWithValuePressed onPressed) {
    return TextButtonWithValue(
      value: this,
      onPressed: onPressed,
      child: Text(title),
    );
  }
}

typedef OnTextButtonWithValuePressed<T> = void Function(T value);

class TextButtonWithValue<T> extends StatelessWidget {
  final T value;
  final OnTextButtonWithValuePressed onPressed;
  final Widget child;
  const TextButtonWithValue({
    Key? key,
    required this.value,
    required this.onPressed,
    required this.child,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return TextButton(
      onPressed: () {
        onPressed(value);
      },
      child: child,
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  OverlayColor? _overlayColor;

  ColorFilter? getcolorFilter() {
    final overlayColor = _overlayColor;
    if (overlayColor == null) {
      return null;
    }
    return ColorFilter.mode(
      overlayColor.color,
      BlendMode.colorBurn,
    );
  }

  Iterable<Widget> overlayColorButtons() {
    return OverlayColor.values.map((overlayColor) {
      return Expanded(
        flex: 1,
        child: Container(
          child: overlayColor.toTextButtonWithValue(
            (value) {
              setState(() {
                _overlayColor = value;
              });
            },
          ),
        ),
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Tinting Images in Flutter'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            Container(
              height: 250.0,
              clipBehavior: Clip.antiAlias,
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(20),
                image: DecorationImage(
                  colorFilter: getcolorFilter(),
                  fit: BoxFit.fitHeight,
                  image: NetworkImage('https://bit.ly/3jOueGG'),
                ),
              ),
            ),
            SizedBox(height: 16.0),
            Row(
              children: overlayColorButtons().toList(),
            )
          ],
        ),
      ),
    );
  }
}

SlideTransition in Flutter



import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';

class _HomePageState extends State<HomePage>
    with SingleTickerProviderStateMixin {
  late final _controller = AnimationController(
    duration: Duration(milliseconds: 500),
    vsync: this,
  );

  late final _animation = Tween<Offset>(
    begin: Offset(0.0, 0.0),
    end: Offset(-0.83, 0.0),
  ).animate(
    CurvedAnimation(
      parent: _controller,
      curve: Curves.easeInQuint,
    ),
  );

  var _isExpanded = false;

  @override
  void dispose() {
    super.dispose();
    _controller.dispose();
  }

  @override
  Widget build(BuildContext context) {
    _controller.forward();
    return Scaffold(
      body: SizedBox.expand(
        child: Stack(
          fit: StackFit.passthrough,
          children: [
            Image.network(
              'https://bit.ly/3BWYDbz',
              fit: BoxFit.fitHeight,
            ),
            Positioned(
              top: 200.0,
              child: SlideTransition(
                position: _animation,
                child: GestureDetector(
                  onTap: () {
                    _isExpanded = !_isExpanded;
                    if (_isExpanded) {
                      _controller.reverse();
                    } else {
                      _controller.forward();
                    }
                  },
                  child: Box(),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class Box extends StatelessWidget {
  const Box({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: Colors.blue[200]?.withAlpha(200),
        border: Border.all(
          color: Colors.blue,
          style: BorderStyle.solid,
          width: 1.0,
        ),
        borderRadius: BorderRadius.only(
          topRight: Radius.circular(10),
          bottomRight: Radius.circular(10),
        ),
      ),
      child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Row(
          children: [
            Text(
              'By: Jesper Anhede',
              style: TextStyle(
                fontSize: 18.0,
              ),
            ),
            SizedBox(width: 10.0),
            Icon(
              Icons.info,
              color: Colors.pink[400],
            ),
          ],
        ),
      ),
    );
  }
}

Expansion Panels and Lists in Flutter



import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

class Event {
  final String title;
  final String details;
  final String imageUrl;
  bool isExpanded = false;

  Event({
    required this.title,
    required this.details,
    required this.imageUrl,
  });

  @override
  bool operator ==(covariant Event other) => title == other.title;
}

const diwaliDetails =
    '''Diwali, or Dipawali, is India's biggest and most important holiday of the year. The festival gets its name from the row (avali) of clay lamps (deepa) that Indians light outside their homes to symbolize the inner light that protects from spiritual darkness. This festival is as important to Hindus as the Christmas holiday is to Christians.''';

const halloweenDetails =
    '''Halloween or Hallowe'en, less commonly known as Allhalloween, All Hallows' Eve, or All Saints' Eve, is a celebration observed in many countries on 31 October, the eve of the Western Christian feast of All Hallows' Day.''';

final events = [
  Event(
    title: 'Diwali',
    details: diwaliDetails,
    imageUrl: 'https://bit.ly/3mGg8YW',
  ),
  Event(
    title: 'Halloween',
    details: halloweenDetails,
    imageUrl: 'https://bit.ly/3wb1w7j',
  ),
];

extension ToPanel on Event {
  ExpansionPanel toPanel() {
    return ExpansionPanel(
      headerBuilder: (context, isExpanded) {
        return Padding(
          padding: const EdgeInsets.all(8.0),
          child: Text(
            title,
            style: TextStyle(fontSize: 30.0),
          ),
        );
      },
      isExpanded: isExpanded,
      body: Container(
        height: 250,
        width: double.infinity,
        decoration: BoxDecoration(
          image: DecorationImage(
            fit: BoxFit.fitWidth,
            colorFilter: ColorFilter.mode(
              Colors.black.withOpacity(0.5),
              BlendMode.luminosity,
            ),
            image: NetworkImage(imageUrl),
          ),
        ),
        child: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Text(
              details,
              textAlign: TextAlign.center,
              style: TextStyle(fontSize: 20, color: Colors.white, shadows: [
                Shadow(
                  blurRadius: 1.0,
                  offset: Offset.zero,
                  color: Colors.black,
                )
              ]),
            ),
          ),
        ),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Expansion Panels in Flutter'),
      ),
      body: SingleChildScrollView(
        child: ExpansionPanelList(
          children: events.map((e) => e.toPanel()).toList(),
          expansionCallback: (panelIndex, isExpanded) {
            setState(() {
              events[panelIndex].isExpanded = !isExpanded;
            });
          },
        ),
      ),
    );
  }
}

Complete CRUD App in Flutter

//Want to support my work 🤝? https://buymeacoffee.com/vandad

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart'
    show getApplicationDocumentsDirectory;

class Person implements Comparable {
  final int id;
  final String firstName;
  final String lastName;
  const Person(this.id, this.firstName, this.lastName);

  String get fullName => '$firstName $lastName';

  Person.fromData(Map<String, Object?> data)
      : id = data['ID'] as int,
        firstName = data['FIRST_NAME'] as String,
        lastName = data['LAST_NAME'] as String;

  @override
  int compareTo(covariant Person other) => other.id.compareTo(id);

  @override
  bool operator ==(covariant Person other) => id == other.id;

  @override
  String toString() =>
      'Person, ID = $id, firstName = $firstName, lastName = $lastName';
}

class PersonDB {
  final _controller = StreamController<List<Person>>.broadcast();
  List<Person> _persons = [];
  Database? _db;
  final String dbName;
  PersonDB({required this.dbName});

  Future<bool> close() async {
    final db = _db;
    if (db == null) {
      return false;
    }
    await db.close();
    return true;
  }

  Future<bool> open() async {
    if (_db != null) {
      return true;
    }
    final directory = await getApplicationDocumentsDirectory();
    final path = '${directory.path}/$dbName';
    try {
      final db = await openDatabase(path);
      _db = db;

      // create the table if it doesn't exist

      final create = '''CREATE TABLE IF NOT EXISTS PEOPLE (
          ID INTEGER PRIMARY KEY AUTOINCREMENT,
          FIRST_NAME STRING NOT NULL,
          LAST_NAME STRING NOT NULL
        )''';

      await db.execute(create);

      // if everything went fine, we then read all the objects
      // and populate the stream

      _persons = await _fetchPeople();
      _controller.add(_persons);
      return true;
    } catch (e) {
      print('error = $e');
      return false;
    }
  }

  Future<List<Person>> _fetchPeople() async {
    final db = _db;
    if (db == null) {
      return [];
    }

    try {
      // read the existing data if any
      final readResult = await db.query(
        'PEOPLE',
        distinct: true,
        columns: ['ID', 'FIRST_NAME', 'LAST_NAME'],
        orderBy: 'ID',
      );

      final people = readResult.map((row) => Person.fromData(row)).toList();
      return people;
    } catch (e) {
      print('error = $e');
      return [];
    }
  }

  Future<bool> delete(Person person) async {
    final db = _db;
    if (db == null) {
      return false;
    }
    try {
      final deletedCount = await db.delete(
        'PEOPLE',
        where: 'ID = ?',
        whereArgs: [person.id],
      );

      // delete it locally as well

      if (deletedCount == 1) {
        _persons.remove(person);
        _controller.add(_persons);
        return true;
      } else {
        return false;
      }
    } catch (e) {
      print('Error inserting $e');
      return false;
    }
  }

  Future<bool> create(String firstName, String lastName) async {
    final db = _db;
    if (db == null) {
      return false;
    }
    try {
      final id = await db.insert(
        'PEOPLE',
        {
          'FIRST_NAME': firstName,
          'LAST_NAME': lastName,
        },
      );

      final person = Person(id, firstName, lastName);
      _persons.add(person);
      _controller.add(_persons);

      return true;
    } catch (e) {
      print('Error inserting $e');
      return false;
    }
  }

  // uses the person's id to update its first name and last name
  Future<bool> update(Person person) async {
    final db = _db;
    if (db == null) {
      return false;
    }
    try {
      final updatedCount = await db.update(
        'PEOPLE',
        {
          'FIRST_NAME': person.firstName,
          'LAST_NAME': person.lastName,
        },
        where: 'ID = ?',
        whereArgs: [person.id],
      );

      if (updatedCount == 1) {
        _persons.removeWhere((p) => p.id == person.id);
        _persons.add(person);
        _controller.add(_persons);
        return true;
      } else {
        return false;
      }
    } catch (e) {
      print('Error inserting $e');
      return false;
    }
  }

  Stream<List<Person>> all() =>
      _controller.stream.map((event) => event..sort());
}

typedef OnCompose = void Function(String firstName, String lastName);

class ComposeWidget extends StatefulWidget {
  final OnCompose onCompose;

  const ComposeWidget({Key? key, required this.onCompose}) : super(key: key);

  @override
  State<ComposeWidget> createState() => _ComposeWidgetState();
}

class _ComposeWidgetState extends State<ComposeWidget> {
  final firstNameController = TextEditingController();
  final lastNameController = TextEditingController();

  @override
  void dispose() {
    firstNameController.dispose();
    lastNameController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(children: [
        TextField(
          style: TextStyle(fontSize: 24),
          decoration: InputDecoration(
            hintText: 'Enter first name',
          ),
          controller: firstNameController,
        ),
        TextField(
          style: TextStyle(fontSize: 24),
          decoration: InputDecoration(
            hintText: 'Enter last name',
          ),
          controller: lastNameController,
        ),
        TextButton(
          onPressed: () {
            final firstName = firstNameController.text;
            final lastName = lastNameController.text;
            widget.onCompose(firstName, lastName);
          },
          child: Text(
            'Add to list',
            style: TextStyle(fontSize: 24),
          ),
        ),
      ]),
    );
  }
}

class HomePage extends StatefulWidget {
  HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  late final PersonDB _crudStorage;

  @override
  void initState() {
    _crudStorage = PersonDB(dbName: 'db.sqlite');
    _crudStorage.open();
    super.initState();
  }

  @override
  void dispose() {
    _crudStorage.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('SQLite in Flutter'),
      ),
      body: StreamBuilder(
        stream: _crudStorage.all(),
        builder: (context, snapshot) {
          switch (snapshot.connectionState) {
            case ConnectionState.active:
            case ConnectionState.waiting:
              if (snapshot.data == null) {
                return Center(child: CircularProgressIndicator());
              }
              final people = snapshot.data as List<Person>;
              return Column(
                children: [
                  ComposeWidget(
                    onCompose: (firstName, lastName) async {
                      await _crudStorage.create(firstName, lastName);
                    },
                  ),
                  Expanded(
                    child: ListView.builder(
                      itemCount: people.length,
                      itemBuilder: (context, index) {
                        final person = people[index];
                        return ListTile(
                          onTap: () async {
                            final update =
                                await showUpdateDialog(context, person);
                            if (update == null) {
                              return;
                            }
                            await _crudStorage.update(update);
                          },
                          title: Text(
                            person.fullName,
                            style: TextStyle(fontSize: 24),
                          ),
                          subtitle: Text(
                            'ID: ${person.id}',
                            style: TextStyle(fontSize: 18),
                          ),
                          trailing: TextButton(
                            onPressed: () async {
                              final shouldDelete =
                                  await showDeleteDialog(context);
                              if (shouldDelete) {
                                await _crudStorage.delete(person);
                              }
                            },
                            child: Icon(
                              Icons.disabled_by_default_rounded,
                              color: Colors.red,
                            ),
                          ),
                        );
                      },
                    ),
                  ),
                ],
              );
            default:
              return Center(child: CircularProgressIndicator());
          }
        },
      ),
    );
  }
}

final firstNameController = TextEditingController();
final lastNameController = TextEditingController();

Future<Person?> showUpdateDialog(BuildContext context, Person person) {
  firstNameController.text = person.firstName;
  lastNameController.text = person.lastName;
  return showDialog(
    context: context,
    builder: (context) {
      return AlertDialog(
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text('Enter your udpated values here:'),
            TextField(controller: firstNameController),
            TextField(controller: lastNameController),
          ],
        ),
        actions: [
          TextButton(
            onPressed: () {
              Navigator.of(context).pop(null);
            },
            child: Text('Cancel'),
          ),
          TextButton(
            onPressed: () {
              final newPerson = Person(
                person.id,
                firstNameController.text,
                lastNameController.text,
              );
              Navigator.of(context).pop(newPerson);
            },
            child: Text('Save'),
          ),
        ],
      );
    },
  ).then((value) {
    if (value is Person) {
      return value;
    } else {
      return null;
    }
  });
}

Future<bool> showDeleteDialog(BuildContext context) {
  return showDialog(
    context: context,
    builder: (context) {
      return AlertDialog(
        content: Text('Are you sure you want to delete this item?'),
        actions: [
          TextButton(
            onPressed: () {
              Navigator.of(context).pop(false);
            },
            child: Text('No'),
          ),
          TextButton(
            onPressed: () {
              Navigator.of(context).pop(true);
            },
            child: Text('Delete'),
          )
        ],
      );
    },
  ).then(
    (value) {
      if (value is bool) {
        return value;
      } else {
        return false;
      }
    },
  );
}

SQLite Storage in Flutter



import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart'
    show getApplicationDocumentsDirectory;

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class Person implements Comparable {
  final int id;
  final String firstName;
  final String lastName;
  const Person(this.id, this.firstName, this.lastName);

  String get fullName => '$firstName $lastName';

  Person.fromData(Map<String, Object?> data)
      : id = data['ID'] as int,
        firstName = data['FIRST_NAME'] as String,
        lastName = data['LAST_NAME'] as String;

  @override
  int compareTo(covariant Person other) => other.id.compareTo(id);
}

typedef OnCompose = void Function(String firstName, String lastName);

class ComposeWidget extends StatefulWidget {
  final OnCompose onCompose;

  const ComposeWidget({Key? key, required this.onCompose}) : super(key: key);

  @override
  State<ComposeWidget> createState() => _ComposeWidgetState();
}

class _ComposeWidgetState extends State<ComposeWidget> {
  final firstNameController = TextEditingController();
  final lastNameController = TextEditingController();

  @override
  void dispose() {
    firstNameController.dispose();
    lastNameController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(children: [
        TextField(
          style: TextStyle(fontSize: 24),
          decoration: InputDecoration(
            hintText: 'Enter first name',
          ),
          controller: firstNameController,
        ),
        TextField(
          style: TextStyle(fontSize: 24),
          decoration: InputDecoration(
            hintText: 'Enter last name',
          ),
          controller: lastNameController,
        ),
        TextButton(
          onPressed: () {
            final firstName = firstNameController.text;
            final lastName = lastNameController.text;
            widget.onCompose(firstName, lastName);
          },
          child: Text(
            'Add to list',
            style: TextStyle(fontSize: 24),
          ),
        ),
      ]),
    );
  }
}

class _HomePageState extends State<HomePage> {
  late final Database db;
  bool hasSetUpAlready = false;

  Future<bool> setupDatabase() async {
    if (hasSetUpAlready == false) {
      final directory = await getApplicationDocumentsDirectory();
      final path = '${directory.path}/db.sqlite';
      try {
        db = await openDatabase(path);

        // create the table if it doesn't exist

        final create = '''CREATE TABLE IF NOT EXISTS PEOPLE (
          ID INTEGER PRIMARY KEY AUTOINCREMENT,
          FIRST_NAME STRING NOT NULL,
          LAST_NAME STRING NOT NULL
        )''';

        await db.execute(create);
        hasSetUpAlready = true;
        return true;
      } catch (e) {
        print('error = $e');
        hasSetUpAlready = false;
        return false;
      }
    } else {
      return true;
    }
  }

  Future<List<Person>> fetchPersons() async {
    if (!await setupDatabase()) {
      return [];
    }

    try {
      // read the existing data if any
      final readResult = await db.query(
        'PEOPLE',
        distinct: true,
        columns: ['ID', 'FIRST_NAME', 'LAST_NAME'],
        orderBy: 'ID',
      );

      final people = readResult.map((row) => Person.fromData(row)).toList()
        ..sort();
      return people;
    } catch (e) {
      print('error = $e');
      return [];
    }
  }

  Future<bool> addPerson(String firstName, String lastName) async {
    try {
      await db.insert(
        'PEOPLE',
        {
          'FIRST_NAME': firstName,
          'LAST_NAME': lastName,
        },
      );
      return true;
    } catch (e) {
      print('Error inserting $e');
      return false;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('SQLite in Flutter'),
      ),
      body: FutureBuilder(
        future: fetchPersons(),
        builder: (context, snapshot) {
          switch (snapshot.connectionState) {
            case ConnectionState.done:
              final people = snapshot.data as List<Person>;
              return Column(
                children: [
                  ComposeWidget(
                    onCompose: (firstName, lastName) async {
                      await addPerson(firstName, lastName);
                      setState(() {});
                    },
                  ),
                  Expanded(
                    child: ListView.builder(
                      itemCount: people.length,
                      itemBuilder: (context, index) {
                        final person = people[index];
                        return ListTile(
                          title: Text(
                            person.fullName,
                            style: TextStyle(fontSize: 24),
                          ),
                          subtitle: Text(
                            'ID: ${person.id}',
                            style: TextStyle(fontSize: 18),
                          ),
                        );
                      },
                    ),
                  ),
                ],
              );
            default:
              return CircularProgressIndicator();
          }
        },
      ),
    );
  }
}

Circular Progress with Percentage in Flutter



import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

class SizedCircularProgressIndicator extends StatelessWidget {
  final double progress;
  final double width;
  final double height;
  final TextStyle? textStyle;
  const SizedCircularProgressIndicator({
    Key? key,
    this.textStyle,
    required this.progress,
    required this.width,
    required this.height,
  }) : super(key: key);

  TextStyle get style => textStyle ?? TextStyle(fontSize: 30.0);
  int get _progress => (progress * 100.0).toInt();

  @override
  Widget build(BuildContext context) {
    return Stack(
      alignment: Alignment.center,
      children: [
        Text('$_progress%', style: style),
        SizedBox(
          width: width,
          height: height,
          child: CircularProgressIndicator(
            backgroundColor: Colors.white70,
            value: progress,
            color: Colors.blueAccent,
            strokeWidth: 3.0,
          ),
        ),
      ],
    );
  }
}

const images = [
  'https://bit.ly/3x7J5Qt',
  'https://bit.ly/3ywI8l6',
  'https://bit.ly/36fNNj9',
  'https://bit.ly/3jOueGG',
  'https://bit.ly/3qYOtDm',
  'https://bit.ly/3wt11Ec',
  'https://bit.ly/3yvFg7X',
  'https://bit.ly/3ywzOla',
  'https://bit.ly/3wnASpW',
  'https://bit.ly/3jXSDto',
];

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: PageView.builder(
        scrollDirection: Axis.horizontal,
        itemCount: images.length,
        itemBuilder: (context, index) {
          return WithBlurredBackground(imageUrl: images[index]);
        },
      ),
    );
  }
}

class WithBlurredBackground extends StatelessWidget {
  final String imageUrl;

  const WithBlurredBackground({Key? key, required this.imageUrl})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SizedBox.expand(
      child: Stack(
        alignment: Alignment.center,
        fit: StackFit.passthrough,
        children: [
          SizedBox.expand(
            child: ClipRect(
              child: ImageFiltered(
                imageFilter: ImageFilter.blur(
                  sigmaX: 10.0,
                  sigmaY: 10.0,
                ),
                child: Image.network(
                  imageUrl,
                  fit: BoxFit.fitHeight,
                ),
              ),
            ),
          ),
          NetworkImageWithProgress(url: imageUrl),
        ],
      ),
    );
  }
}

class NetworkImageWithProgress extends StatelessWidget {
  final String url;

  const NetworkImageWithProgress({Key? key, required this.url})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Image.network(
      url,
      loadingBuilder: (context, child, loadingProgress) {
        final totalBytes = loadingProgress?.expectedTotalBytes;
        final bytesLoaded = loadingProgress?.cumulativeBytesLoaded;
        if (totalBytes != null && bytesLoaded != null) {
          return SizedCircularProgressIndicator(
            height: 100,
            width: 100,
            progress: bytesLoaded / totalBytes,
          );
        } else {
          return child;
        }
      },
      errorBuilder: (context, error, stackTrace) {
        return Text('Error!');
      },
    );
  }
}

Opening URLs in Flutter



import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';

class Person {
  final String profileUrl;
  final String name;
  final String email;
  final String phoneNumber;
  final String websiteUrl;

  const Person({
    Key? key,
    required this.profileUrl,
    required this.name,
    required this.email,
    required this.phoneNumber,
    required this.websiteUrl,
  });

  Person.vandad()
      : profileUrl = 'https://bit.ly/3jwusS0',
        name = 'Vandad Nahavandipoor',
        email = 'vandad.np@gmail.com',
        phoneNumber = '071234567',
        websiteUrl = 'https://pixolity.se';
}

void open(String url) async {
  if (await canLaunch(url)) {
    await launch(url);
  }
}

class PersonNameView extends StatelessWidget {
  final Person person;
  const PersonNameView(this.person, {Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(
      person.name,
      style: TextStyle(
        fontSize: 20,
        fontWeight: FontWeight.bold,
        color: Colors.black,
      ),
    );
  }
}

class PersonEmailView extends StatelessWidget {
  final Person person;
  const PersonEmailView(this.person, {Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return TextButton(
      onPressed: () {
        open('mailto:${person.email}');
      },
      child: Text(
        '💌 Email me',
        style: TextStyle(fontSize: 20.0),
      ),
    );
  }
}

class PersonPhoneView extends StatelessWidget {
  final Person person;
  const PersonPhoneView(this.person, {Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return TextButton(
      onPressed: () {
        open('tel://${person.phoneNumber}');
      },
      child: Text(
        '🤙🏻 Call me',
        style: TextStyle(fontSize: 20.0),
      ),
    );
  }
}

class PersonWebsiteView extends StatelessWidget {
  final Person person;
  const PersonWebsiteView(this.person, {Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return TextButton(
      onPressed: () {
        open(person.websiteUrl);
      },
      child: Text(
        '🌍 Visit my website',
        style: TextStyle(fontSize: 20.0),
      ),
    );
  }
}

const bigText = TextStyle(fontSize: 20.0);

class PersonView extends StatelessWidget {
  final Person person;
  const PersonView({Key? key, required this.person}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
          color: Colors.blue[50],
          borderRadius: BorderRadius.circular(16.0),
          border: Border.all(color: Colors.white, width: 3.0),
          boxShadow: [
            BoxShadow(
              blurRadius: 30.0,
              color: Colors.black.withAlpha(50),
              spreadRadius: 20.0,
            ),
          ]),
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            CircleAvatar(
              radius: 100.0,
              backgroundImage: NetworkImage(person.profileUrl),
            ),
            SizedBox(height: 10),
            PersonNameView(person),
            PersonEmailView(person),
            PersonPhoneView(person),
            PersonWebsiteView(person)
          ],
        ),
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: SingleChildScrollView(
          child: PersonView(
            person: Person.vandad(),
          ),
        ),
      ),
    );
  }
}

Commodore 64 Screen in Flutter



import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

const textColor = Color.fromRGBO(156, 156, 247, 1);

class BoxPainter extends CustomPainter {
  final bool isBlinking;
  BoxPainter({required this.isBlinking});

  @override
  void paint(Canvas canvas, Size size) {
    if (isBlinking) {
      final rect = Rect.fromLTWH(
        size.width / 20.0,
        size.height / 2.8,
        size.width / 24.0,
        size.height / 13.0,
      );
      final paint = Paint()..color = textColor;
      canvas.drawRect(rect, paint);
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

class ReadyPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final textStyle = TextStyle(
      color: textColor,
      fontSize: size.width / 45.0,
      fontFamily: 'C64',
    );
    final span = TextSpan(
      text: 'READY',
      style: textStyle,
    );
    final painter = TextPainter(
      text: span,
      textDirection: TextDirection.ltr,
    );

    painter.layout(
      minWidth: 0,
      maxWidth: size.width,
    );
    final offset = Offset(
      painter.width / 2.0,
      painter.height * 8.0,
    );
    painter.paint(canvas, offset);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

class SubTitlePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final textStyle = TextStyle(
      color: textColor,
      fontSize: size.width / 45.0,
      fontFamily: 'C64',
    );
    final span = TextSpan(
      text: '64K RAM SYSTEM 38911 BASIC BYTES FREE',
      style: textStyle,
    );
    final painter = TextPainter(
      text: span,
      textDirection: TextDirection.ltr,
    );

    painter.layout(
      minWidth: 0,
      maxWidth: size.width,
    );
    final offset = Offset(
      size.width - painter.width - (size.width / 11),
      painter.height * 6.0,
    );
    painter.paint(canvas, offset);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

class TitlePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final textStyle = TextStyle(
      color: textColor,
      fontSize: size.width / 40.0,
      fontFamily: 'C64',
    );
    final span = TextSpan(
      text: '**** COMMODORE 64 BASIC V2 ****',
      style: textStyle,
    );
    final painter = TextPainter(
      text: span,
      textDirection: TextDirection.ltr,
    );

    painter.layout(
      minWidth: 0,
      maxWidth: size.width,
    );
    final offset = Offset(
      size.width - painter.width - (size.width / 9),
      size.height / 6,
    );
    painter.paint(canvas, offset);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

class BackgroundPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final border = size.width / 20.0;

    final color = Color.fromRGBO(
      58,
      57,
      213,
      1,
    );

    final paint = Paint()..color = color;

    final rect = Rect.fromLTWH(
      border,
      border,
      size.width - (border * 2.0),
      size.height - (border * 2.0),
    );

    canvas.drawRect(rect, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

class BorderPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()..color = textColor;
    final rect = Rect.fromLTWH(
      0.0,
      0.0,
      size.width,
      size.height,
    );

    canvas.drawRect(rect, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

class Commodore64Painter extends CustomPainter {
  final bool isBlinking;

  late final List<CustomPainter> painters;

  Commodore64Painter({required this.isBlinking}) {
    painters = [
      BorderPainter(),
      BackgroundPainter(),
      TitlePainter(),
      SubTitlePainter(),
      ReadyPainter(),
      BoxPainter(isBlinking: isBlinking)
    ];
  }

  @override
  void paint(Canvas canvas, Size size) {
    painters.forEach(
      (p) => p.paint(
        canvas,
        size,
      ),
    );
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => painters
      .map((p) => p.shouldRepaint(oldDelegate))
      .reduce((p1, p2) => p1 || p2);
}

class Commodore64 extends StatefulWidget {
  const Commodore64({Key? key}) : super(key: key);

  @override
  State<Commodore64> createState() => _Commodore64State();
}

class _Commodore64State extends State<Commodore64> {
  bool _isBlinking = false;

  final timer = Stream.periodic(Duration(seconds: 1), (value) => value);

  void _triggerRedraw() async {
    await for (final _ in timer) {
      setState(() {
        _isBlinking = !_isBlinking;
      });
    }
  }

  @override
  void initState() {
    super.initState();
    _triggerRedraw();
  }

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(builder: (context, constraints) {
      return Container(
        width: constraints.maxWidth,
        height: constraints.maxWidth / (16.0 / 9.0),
        child: CustomPaint(
          painter: Commodore64Painter(isBlinking: _isBlinking),
        ),
      );
    });
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Commodore64(),
      ),
    );
  }
}

Animated Lists in Flutter



import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

class NetworkImage extends StatelessWidget {
  final String url;
  const NetworkImage({
    Key? key,
    required this.url,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Image.network(
      url,
      loadingBuilder: (context, child, loadingProgress) {
        final totalBytes = loadingProgress?.expectedTotalBytes;
        final bytesLoaded = loadingProgress?.cumulativeBytesLoaded;
        if (totalBytes != null && bytesLoaded != null) {
          return LinearProgressIndicator(
            value: bytesLoaded / totalBytes,
          );
        } else {
          return child;
        }
      },
      errorBuilder: (context, error, stackTrace) {
        return Text('Error!');
      },
    );
  }
}

class NetworkImageCard extends StatelessWidget {
  final String url;
  const NetworkImageCard({
    Key? key,
    required this.url,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        clipBehavior: Clip.antiAlias,
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(20.0),
        ),
        child: NetworkImage(
          url: url,
        ),
      ),
    );
  }
}

const imageUrls = [
  'https://bit.ly/3x7J5Qt',
  'https://bit.ly/3ywI8l6',
  'https://bit.ly/36fNNj9',
  'https://bit.ly/3jOueGG',
  'https://bit.ly/3qYOtDm',
  'https://bit.ly/3wt11Ec',
  'https://bit.ly/3yvFg7X',
  'https://bit.ly/3ywzOla',
  'https://bit.ly/3wnASpW',
  'https://bit.ly/3jXSDto',
];

Stream<String> images() => Stream.periodic(
      Duration(seconds: 3),
      (index) => index % imageUrls.length,
    ).map((index) => imageUrls[index]);

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final GlobalKey<AnimatedListState> _key = GlobalKey();

  List<String> imageUrls = [];

  void populateList() async {
    await for (final url in images()) {
      imageUrls.insert(0, url);
      _key.currentState?.insertItem(
        0,
        duration: Duration(milliseconds: 400),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    populateList();
    return Scaffold(
      appBar: AppBar(
        title: Text('Animated Lists in Flutter'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: AnimatedList(
          key: _key,
          initialItemCount: imageUrls.length,
          itemBuilder: (context, index, animation) {
            final imageUrl = imageUrls[index];
            return SizeTransition(
              sizeFactor: animation.drive(
                CurveTween(curve: Curves.elasticInOut),
              ),
              child: Column(
                children: [
                  NetworkImageCard(url: imageUrl),
                  SizedBox(height: 10.0),
                ],
              ),
            );
          },
        ),
      ),
    );
  }
}

CheckboxListTile in Flutter

import 'package:flutter/material.dart';



enum Item { umbrella, coat, boots }

extension Info on Item {
  String get title {
    switch (this) {
      case Item.umbrella:
        return 'Umbrella';
      case Item.boots:
        return 'Boots';
      case Item.coat:
        return 'Jacket';
    }
  }

  String get icon {
    switch (this) {
      case Item.umbrella:
        return '☂️';
      case Item.boots:
        return '🥾';
      case Item.coat:
        return '🧥';
    }
  }
}

typedef OnChecked = void Function(bool);

class Tile extends StatelessWidget {
  final Item item;
  final bool isChecked;
  final OnChecked onChecked;
  const Tile({
    Key? key,
    required this.item,
    required this.isChecked,
    required this.onChecked,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ConstrainedBox(
      constraints: BoxConstraints(minHeight: 100.0),
      child: Center(
        child: CheckboxListTile(
          shape: ContinuousRectangleBorder(),
          value: isChecked,
          secondary: Text(
            item.icon,
            style: TextStyle(fontSize: 30.0),
          ),
          title: Text(
            item.title,
            style: TextStyle(fontSize: 40.0),
          ),
          onChanged: (value) {
            onChecked(value ?? false);
          },
        ),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final Set<Item> _enabledItems = {};

  Widget get listView {
    return ListView.builder(
      shrinkWrap: true,
      itemBuilder: (context, index) {
        final item = Item.values[index];
        final isChecked = _enabledItems.contains(item);
        return Tile(
          isChecked: isChecked,
          item: item,
          onChecked: (isChecked) {
            setState(() {
              if (isChecked) {
                _enabledItems.add(item);
              } else {
                _enabledItems.remove(item);
              }
            });
          },
        );
      },
      itemCount: Item.values.length,
    );
  }

  Widget get title {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Text(
        "Remember to pick all items! It's going to be rainy today!",
        textAlign: TextAlign.center,
        style: TextStyle(fontSize: 40),
      ),
    );
  }

  Widget get readyButton {
    final onPressed = () async {
      // program this
      await showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            content: Text('You seem to be ready for a rainy day! ✅'),
            actions: [
              TextButton(
                onPressed: () {
                  Navigator.of(context).pop();
                },
                child: Text('OK'),
              )
            ],
          );
        },
      );
    };

    final isEnabled = _enabledItems.containsAll(Item.values);

    return FractionallySizedBox(
      widthFactor: 0.8,
      child: ElevatedButton(
        onPressed: isEnabled ? onPressed : null,
        child: Text('Ready!'),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Checkbox List Tile in Flutter'),
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            title,
            listView,
            SizedBox(height: 50.0),
            readyButton,
          ],
        ),
      ),
    );
  }
}

- Operator on String in Dart



extension Minus on String {
  String operator -(String rhs) => replaceAll(rhs, '');
}

void testIt() {
  assert('foo bar' - 'foo ' == 'bar');
  assert('foo bar foo' - 'foo' == ' bar ');
  assert('bar' - 'foo' == 'bar');
  assert('BAR' - 'bar' == 'BAR');
  assert('foo' - 'FOO' == 'foo');
  assert('foobarbaz' - 'bar' == 'foobaz');
}

Dart Progress for Future<T>



import 'dart:io' show stdout;
import 'dart:async' show Future, Stream;

const loadingSequence = ['⢿', '⣻', '⣽', '⣾', '⣷', '⣯', '⣟', '⡿'];
const escape = '\x1B[38;5;';
const color = '${escape}1m';
const textColor = '${escape}6m';

String progress({required int value, required String text}) {
  final progress = '$color${loadingSequence[value % loadingSequence.length]}';
  final coloredText = '$textColor$text';
  return '$progress\t$coloredText';
}

Future<T> performWithProgress<T>({
  required Future<T> task,
  required String progressText,
}) {
  final stream = Stream<String>.periodic(
    Duration(milliseconds: 100),
    (value) => progress(
      value: value,
      text: progressText,
    ),
  );

  final subscription = stream.listen(
    (event) {
      stdout.write('\r $event');
    },
  );

  task.whenComplete(() {
    stdout.write('\r ✅\t$progressText');
    stdout.write('\n');
    subscription.cancel();
  });

  return task;
}

final task1 = Future.delayed(Duration(seconds: 1), () => 'Result 1');
final task2 = Future.delayed(Duration(seconds: 2), () => 'Result 2');
final task3 = Future.delayed(Duration(seconds: 3), () => 'Result 3');

void main(List<String> args) async {
  var result = await performWithProgress(
    task: task1,
    progressText: 'Loading task 1',
  );
  print('\tTask 1 result: $result');
  result = await performWithProgress(
    task: task2,
    progressText: 'Loading task 2',
  );
  print('\tTask 2 result: $result');
  result = await performWithProgress(
    task: task3,
    progressText: 'Loading task 3',
  );
  print('\tTask 3 result: $result');
}

Move Widget Shadows with Animation



import 'package:flutter/material.dart';

class ImageTransition extends AnimatedWidget {
  final String imageUrl;

  Animation<double> get shadowXOffset => listenable as Animation<double>;

  const ImageTransition(this.imageUrl, {shadowXOffset})
      : super(listenable: shadowXOffset);

  @override
  Widget build(BuildContext context) {
    return Container(
      clipBehavior: Clip.antiAlias,
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(20.0),
        boxShadow: [
          BoxShadow(
            blurRadius: 10,
            offset: Offset(shadowXOffset.value, 20.0),
            color: Colors.black.withAlpha(100),
            spreadRadius: -10,
          )
        ],
      ),
      child: Image.network(imageUrl),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class CustomCurve extends CurveTween {
  CustomCurve() : super(curve: Curves.easeInOutSine);
  @override
  double transform(double t) {
    return (super.transform(t) - 0.5) * 25.0;
  }
}

class _HomePageState extends State<HomePage>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller;
  late final Animation<double> _animation;

  @override
  void initState() {
    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 1),
    );
    _animation = CustomCurve().animate(_controller);
    super.initState();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    _controller.repeat(reverse: true);
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Center(
          child: ImageTransition(
            'https://bit.ly/3x7J5Qt',
            shadowXOffset: _animation,
          ),
        ),
      ),
    );
  }
}

Gallery with Blurred Backgrounds in Flutter


import 'package:flutter/material.dart';

const images = [
  'https://bit.ly/3x7J5Qt',
  'https://bit.ly/3ywI8l6',
  'https://bit.ly/36fNNj9',
  'https://bit.ly/3jOueGG',
  'https://bit.ly/3qYOtDm',
  'https://bit.ly/3wt11Ec',
  'https://bit.ly/3yvFg7X',
  'https://bit.ly/3ywzOla',
  'https://bit.ly/3wnASpW',
  'https://bit.ly/3jXSDto',
];

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: PageView.builder(
        scrollDirection: Axis.horizontal,
        itemCount: images.length,
        itemBuilder: (context, index) {
          return WithBlurredBackground(imageUrl: images[index]);
        },
      ),
    );
  }
}

class WithBlurredBackground extends StatelessWidget {
  final String imageUrl;

  const WithBlurredBackground({Key? key, required this.imageUrl})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SizedBox.expand(
      child: Stack(
        alignment: Alignment.center,
        fit: StackFit.passthrough,
        children: [
          SizedBox.expand(
            child: ClipRect(
              child: ImageFiltered(
                imageFilter: ImageFilter.blur(
                  sigmaX: 10.0,
                  sigmaY: 10.0,
                ),
                child: Image.network(
                  imageUrl,
                  fit: BoxFit.fitHeight,
                ),
              ),
            ),
          ),
          Image.network(imageUrl),
        ],
      ),
    );
  }
}

Custom Path Clippers in Flutter



import 'package:flutter/material.dart';

const images = [
  'https://bit.ly/3x7J5Qt',
  'https://bit.ly/3ywI8l6',
  'https://bit.ly/36fNNj9',
  'https://bit.ly/3jOueGG',
  'https://bit.ly/3qYOtDm',
  'https://bit.ly/3wt11Ec',
  'https://bit.ly/3yvFg7X',
  'https://bit.ly/3ywzOla',
  'https://bit.ly/3wnASpW',
  'https://bit.ly/3jXSDto',
];

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.only(
            top: 40.0,
            left: 10.0,
            right: 10.0,
          ),
          child: Column(
            children: images
                .map((url) => ElevatedNetworkImage(url: url))
                .expand(
                  (img) => [
                    img,
                    SizedBox(height: 30.0),
                  ],
                )
                .toList(),
          ),
        ),
      ),
    );
  }
}

class ElevatedNetworkImage extends StatelessWidget {
  final String url;
  const ElevatedNetworkImage({Key? key, required this.url}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return PhysicalShape(
      color: Colors.white,
      clipper: Clipper(),
      elevation: 20.0,
      clipBehavior: Clip.none,
      shadowColor: Colors.white.withAlpha(200),
      child: CutEdges(
        child: Image.network(url),
      ),
    );
  }
}

class Clipper extends CustomClipper<Path> {
  static const variance = 0.2;
  static const reverse = 1.0 - variance;

  @override
  Path getClip(Size size) {
    final path = Path();

    path.moveTo(0.0, size.height * Clipper.variance);
    path.lineTo(size.width * Clipper.variance, 0.0);
    path.lineTo(size.width, 0.0);
    path.lineTo(size.width, size.height * Clipper.reverse);
    path.lineTo(size.width * Clipper.reverse, size.height);
    path.lineTo(0.0, size.height);
    path.lineTo(0.0, size.height * Clipper.variance);
    path.close;
    return path;
  }

  @override
  bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}

class CutEdges extends StatelessWidget {
  final Widget child;

  const CutEdges({Key? key, required this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ClipPath(
      clipper: Clipper(),
      child: child,
    );
  }
}

Frost Effect on Images in Flutter



import 'package:flutter/material.dart';

const images = [
  'https://bit.ly/3x7J5Qt',
  'https://bit.ly/3ywI8l6',
  'https://bit.ly/36fNNj9',
  'https://bit.ly/3jOueGG',
  'https://bit.ly/3qYOtDm',
  'https://bit.ly/3wt11Ec',
  'https://bit.ly/3yvFg7X',
  'https://bit.ly/3ywzOla',
  'https://bit.ly/3wnASpW',
  'https://bit.ly/3jXSDto',
];

final loremIpsum =
    'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Padding(
          padding: const EdgeInsets.fromLTRB(
            8.0,
            0.0,
            8.0,
            0.0,
          ),
          child: SingleChildScrollView(
            child: Column(
              children: images
                  .map(
                    (url) => GlossyNetworkImageWithProgress(
                      url: url,
                      title: 'Image title',
                      description: loremIpsum,
                    ),
                  )
                  .expand(
                    (image) => [
                      image,
                      SizedBox(height: 16.0),
                    ],
                  )
                  .toList(),
            ),
          ),
        ),
      ),
    );
  }
}

class GlossyNetworkImageWithProgress extends StatefulWidget {
  final String url;
  final String title;
  final String description;

  const GlossyNetworkImageWithProgress(
      {Key? key,
      required this.url,
      required this.title,
      required this.description})
      : super(key: key);

  @override
  _GlossyNetworkImageWithProgressState createState() =>
      _GlossyNetworkImageWithProgressState();
}

class _GlossyNetworkImageWithProgressState
    extends State<GlossyNetworkImageWithProgress>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller;
  late final Animation<double> _animation;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 1),
    );

    _animation = Tween(
      begin: 0.0,
      end: 1.0,
    ).animate(_controller);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final networkImage = Image.network(
      widget.url,
      fit: BoxFit.fitHeight,
      frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
        _controller.reset();
        _controller.forward();
        return FadeTransition(
          opacity: _animation,
          child: CustomBox(
            child: child,
          ),
        );
      },
      loadingBuilder: (context, child, loadingProgress) {
        final totalBytes = loadingProgress?.expectedTotalBytes;
        final bytesLoaded = loadingProgress?.cumulativeBytesLoaded;
        if (totalBytes != null && bytesLoaded != null) {
          return CustomBox(
            child: CircularProgressIndicator(
              backgroundColor: Colors.white70,
              value: bytesLoaded / totalBytes,
              color: Colors.blue[900],
              strokeWidth: 5.0,
            ),
          );
        } else {
          return child;
        }
      },
      errorBuilder: (context, error, stackTrace) {
        return Text('Error!');
      },
    );

    return BottomGloss(
      networkImage: networkImage,
      title: widget.title,
      description: widget.description,
    );
  }
}

class BottomGloss extends StatelessWidget {
  final String title;
  final String description;

  const BottomGloss(
      {Key? key,
      required this.networkImage,
      required this.title,
      required this.description})
      : super(key: key);

  final Image networkImage;

  @override
  Widget build(BuildContext context) {
    return ClipRRect(
      borderRadius: BorderRadius.all(Radius.circular(8.0)),
      child: Stack(
        fit: StackFit.passthrough,
        children: [
          networkImage,
          Container(
            height: 300.0,
            alignment: Alignment.bottomCenter,
            child: ClipRect(
              child: FractionallySizedBox(
                heightFactor: 0.5,
                child: BackdropFilter(
                  filter: ImageFilter.blur(
                    sigmaX: 10.0,
                    sigmaY: 10.0,
                  ),
                  child: BottomContents(
                    title: title,
                    description: description,
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class BottomContents extends StatelessWidget {
  final String title;
  final String description;

  const BottomContents({
    Key? key,
    required this.title,
    required this.description,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white.withOpacity(0.4),
      child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: SingleChildScrollView(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              TitleText(text: title),
              SizedBox(height: 8.0),
              SubTitleText(text: description),
            ],
          ),
        ),
      ),
    );
  }
}

class SubTitleText extends StatelessWidget {
  final String text;
  const SubTitleText({Key? key, required this.text}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(
      text,
      style: TextStyle(
        color: Colors.black,
        fontSize: 20.0,
      ),
    );
  }
}

class TitleText extends StatelessWidget {
  final String text;
  const TitleText({Key? key, required this.text}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(
      text,
      style: TextStyle(
        color: Colors.white,
        fontSize: 30.0,
      ),
    );
  }
}

class CustomBox extends StatelessWidget {
  final Widget child;

  const CustomBox({Key? key, required this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 300.0,
      width: MediaQuery.of(context).size.width,
      child: child is ProgressIndicator ? Center(child: child) : child,
    );
  }
}

Custom Clippers in Flutter

import 'package:flutter/material.dart';
import 'dart:math' show min;

const gridImages = [
  'https://bit.ly/3x7J5Qt',
  'https://bit.ly/3ywI8l6',
  'https://bit.ly/3jRSRCu',
  'https://bit.ly/36fNNj9',
  'https://bit.ly/3jOueGG',
  'https://bit.ly/3qYOtDm',
  'https://bit.ly/3wt11Ec',
  'https://bit.ly/3yvFg7X',
  'https://bit.ly/3ywzOla',
  'https://bit.ly/3wnASpW',
  'https://bit.ly/3jXSDto',
];

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GridView.count(
        padding: const EdgeInsets.fromLTRB(8.0, 48.0, 8.0, 48.0),
        crossAxisCount: 2,
        mainAxisSpacing: 8.0,
        crossAxisSpacing: 8.0,
        children: gridImages
            .map((url) => NetworkImageWithProgress(url: url))
            .toList(),
      ),
    );
  }
}

class CircularClipper extends CustomClipper<Rect> {
  @override
  Rect getClip(Size size) {
    final center = Offset(
      size.width / 2.0,
      size.height / 2.0,
    );
    final minWidthorHeight = min(size.width, size.height);
    return Rect.fromCenter(
      center: center,
      width: minWidthorHeight,
      height: minWidthorHeight,
    );
  }

  @override
  bool shouldReclip(covariant CustomClipper<Rect> oldClipper) => false;
}

class Circular extends StatelessWidget {
  final Widget child;
  const Circular({Key? key, required this.child}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return ClipOval(
      clipper: CircularClipper(),
      child: child,
    );
  }
}

class CustomBox extends StatelessWidget {
  final Widget child;

  const CustomBox({Key? key, required this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 220.0,
      width: MediaQuery.of(context).size.width,
      child: child is ProgressIndicator
          ? Center(child: child)
          : Circular(child: child),
    );
  }
}

class NetworkImageWithProgress extends StatefulWidget {
  final String url;

  const NetworkImageWithProgress({Key? key, required this.url})
      : super(key: key);

  @override
  _NetworkImageWithProgressState createState() =>
      _NetworkImageWithProgressState();
}

class _NetworkImageWithProgressState extends State<NetworkImageWithProgress>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller;
  late final Animation<double> _animation;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 1),
    );

    _animation = Tween(
      begin: 0.0,
      end: 1.0,
    ).animate(_controller);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Image.network(
      widget.url,
      fit: BoxFit.fitHeight,
      frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
        _controller.reset();
        _controller.forward();
        return FadeTransition(
          opacity: _animation,
          child: CustomBox(
            child: child,
          ),
        );
      },
      loadingBuilder: (context, child, loadingProgress) {
        final totalBytes = loadingProgress?.expectedTotalBytes;
        final bytesLoaded = loadingProgress?.cumulativeBytesLoaded;
        if (totalBytes != null && bytesLoaded != null) {
          return CustomBox(
            child: CircularProgressIndicator(
              backgroundColor: Colors.white70,
              value: bytesLoaded / totalBytes,
              color: Colors.blue[900],
              strokeWidth: 5.0,
            ),
          );
        } else {
          return child;
        }
      },
      errorBuilder: (context, error, stackTrace) {
        return Text('Error!');
      },
    );
  }
}

Check if Website is Up or Down in Dart



class UpStatus {
  final bool isUp;
  final DateTime timestamp;
  const UpStatus(this.isUp, this.timestamp);
}

class Pling {
  final String url;
  final Duration interval;
  const Pling({
    required this.url,
    required this.interval,
  });
  Stream<UpStatus> checkIfUp() =>
      Stream.periodic(interval, (_) => DateTime.now()).asyncExpand(
        (now) => HttpClient()
            .headUrl(Uri.parse(url))
            .then((req) => req..followRedirects = false)
            .then((req) => req.close())
            .then((resp) => resp.statusCode)
            .then((statusCode) => statusCode == 200)
            .onError((error, stackTrace) => false)
            .then((isUp) => UpStatus(isUp, now))
            .asStream(),
      );
}

const oneSecond = Duration(seconds: 1);
const url = 'https://dart.dev';

extension IsOrIsNot on bool {
  String get isOrIsNot => this ? 'is' : 'is not';
}

void testIt() async {
  final pling = Pling(
    url: url,
    interval: oneSecond,
  );
  await for (final upStatus in pling.checkIfUp()) {
    final timestamp = upStatus.timestamp;
    final isUpStr = upStatus.isUp.isOrIsNot;
    print('$url $isUpStr up at $timestamp');
  }
}

Section Titles on ListView in Flutter

import 'package:flutter/material.dart';

final List<Section> allSections = [
  Section(
    'Spring',
    [
      'https://cnn.it/3xu58Ap',
      'https://bit.ly/3ueqqC1',
    ],
  ),
  Section(
    'Summer',
    [
      'https://bit.ly/3ojNhLc',
      'https://bit.ly/2VcCSow',
    ],
  ),
  Section(
    'Autumn',
    [
      'https://bit.ly/3ib1TJk',
      'https://bit.ly/2XSpjvq',
    ],
  ),
  Section(
    'Winter',
    [
      'https://bit.ly/3iaQNE7',
      'https://bit.ly/3AY8YE4',
    ],
  ),
];

class Section {
  final String title;
  final List<String> urls;
  const Section(this.title, this.urls);
}

extension ToWidgets on Section {
  Iterable<Widget> toNetworkImageCards() {
    return [
      Padding(
        padding: const EdgeInsets.all(8.0),
        child: Text(
          title,
          style: TextStyle(
            fontSize: 40,
          ),
        ),
      ),
      ...urls.expand(
        (url) => [
          NetworkImageCard(url: url),
          SizedBox(height: 10),
        ],
      ),
    ];
  }
}

class NetworkImageCard extends StatelessWidget {
  final String url;
  const NetworkImageCard({
    Key? key,
    required this.url,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        child: NetworkImageWithProgress(
          url: url,
        ),
      ),
    );
  }
}

class NetworkImageWithProgress extends StatefulWidget {
  final String url;

  const NetworkImageWithProgress({Key? key, required this.url})
      : super(key: key);

  @override
  _NetworkImageWithProgressState createState() =>
      _NetworkImageWithProgressState();
}

class _NetworkImageWithProgressState extends State<NetworkImageWithProgress>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller;
  late final Animation<double> _animation;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 1),
    );

    _animation = Tween(
      begin: 0.0,
      end: 1.0,
    ).animate(_controller);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Image.network(
      widget.url,
      fit: BoxFit.fitWidth,
      frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
        _controller.reset();
        _controller.forward();
        return FadeTransition(
          opacity: _animation,
          child: CustomBox(
            child: child,
          ),
        );
      },
      loadingBuilder: (context, child, loadingProgress) {
        final totalBytes = loadingProgress?.expectedTotalBytes;
        final bytesLoaded = loadingProgress?.cumulativeBytesLoaded;
        if (totalBytes != null && bytesLoaded != null) {
          return CustomBox(
            child: CircularProgressIndicator(
              backgroundColor: Colors.white70,
              value: bytesLoaded / totalBytes,
              color: Colors.blue[900],
              strokeWidth: 5.0,
            ),
          );
        } else {
          return child;
        }
      },
      errorBuilder: (context, error, stackTrace) {
        return Text('Error!');
      },
    );
  }
}

class CustomBox extends StatelessWidget {
  final Widget child;

  const CustomBox({Key? key, required this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 220.0,
      width: MediaQuery.of(context).size.width,
      child: child is ProgressIndicator ? Center(child: child) : child,
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView.builder(
        itemBuilder: (context, index) {
          final section = allSections[index];
          return Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: section.toNetworkImageCards().toList(),
          );
        },
        itemCount: allSections.length,
      ),
    );
  }
}

Circular Progress in Flutter



import 'package:flutter/material.dart';

class CustomBox extends StatelessWidget {
  final Widget child;

  const CustomBox({Key? key, required this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 220.0,
      width: MediaQuery.of(context).size.width,
      child: Center(child: child),
    );
  }
}

class NetworkImageWithProgress extends StatefulWidget {
  final String url;

  const NetworkImageWithProgress({Key? key, required this.url})
      : super(key: key);

  @override
  _NetworkImageWithProgressState createState() =>
      _NetworkImageWithProgressState();
}

class _NetworkImageWithProgressState extends State<NetworkImageWithProgress>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller;
  late final Animation<double> _animation;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 1),
    );

    _animation = Tween(
      begin: 0.0,
      end: 1.0,
    ).animate(_controller);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Image.network(
      widget.url,
      fit: BoxFit.fitWidth,
      frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
        _controller.reset();
        _controller.forward();
        return FadeTransition(
          opacity: _animation,
          child: CustomBox(
            child: child,
          ),
        );
      },
      loadingBuilder: (context, child, loadingProgress) {
        final totalBytes = loadingProgress?.expectedTotalBytes;
        final bytesLoaded = loadingProgress?.cumulativeBytesLoaded;
        if (totalBytes != null && bytesLoaded != null) {
          return CustomBox(
            child: CircularProgressIndicator(
              backgroundColor: Colors.white70,
              value: bytesLoaded / totalBytes,
              color: Colors.blue[900],
              strokeWidth: 5.0,
            ),
          );
        } else {
          return child;
        }
      },
      errorBuilder: (context, error, stackTrace) {
        return Text('Error!');
      },
    );
  }
}

final images = [
  'https://bit.ly/3x7J5Qt',
  'https://bit.ly/3ywI8l6',
  'https://bit.ly/36fNNj9',
  'https://bit.ly/3jOueGG',
  'https://bit.ly/3wt11Ec',
  'https://bit.ly/3ywzOla',
].map((url) => NetworkImageWithProgress(url: url)).expand(
      (element) => [
        element,
        SizedBox(
          height: 10.0,
        )
      ],
    );

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
        child: Column(
          children: images.toList(),
        ),
      ),
    );
  }
}

Displaying Scroll Wheels in Flutter



import 'package:flutter/material.dart';

class FadingNetworkImage extends StatefulWidget {
  final String url;

  const FadingNetworkImage({Key? key, required this.url}) : super(key: key);

  @override
  _FadingNetworkImageState createState() => _FadingNetworkImageState();
}

class _FadingNetworkImageState extends State<FadingNetworkImage>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller;
  late final Animation<double> _animation;

  @override
  void initState() {
    super.initState();

    _controller =
        AnimationController(vsync: this, duration: Duration(seconds: 1));

    _animation = Tween(begin: 0.0, end: 1.0).animate(_controller);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Image.network(
      widget.url,
      frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
        _controller.reset();
        _controller.forward();
        return FadeTransition(
          opacity: _animation,
          child: child,
        );
      },
      loadingBuilder: (context, child, loadingProgress) {
        final totalBytes = loadingProgress?.expectedTotalBytes;
        final bytesLoaded = loadingProgress?.cumulativeBytesLoaded;
        if (totalBytes != null && bytesLoaded != null) {
          return LinearProgressIndicator(
            value: bytesLoaded / totalBytes,
          );
        } else {
          return child;
        }
      },
      errorBuilder: (context, error, stackTrace) {
        return Text('Error!');
      },
    );
  }
}

final images = [
  'https://bit.ly/3x7J5Qt',
  'https://bit.ly/3ywI8l6',
  'https://bit.ly/36fNNj9',
  'https://bit.ly/3jOueGG',
  'https://bit.ly/3wt11Ec',
  'https://bit.ly/3ywzOla',
].map((i) => NetworkImageCard(url: i));

class NetworkImageCard extends StatelessWidget {
  final String url;
  const NetworkImageCard({
    Key? key,
    required this.url,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        clipBehavior: Clip.antiAlias,
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(20.0),
          boxShadow: [
            BoxShadow(
              blurRadius: 5,
              offset: Offset(0, 0),
              color: Colors.black.withAlpha(40),
              spreadRadius: 5,
            )
          ],
        ),
        child: FadingNetworkImage(
          url: url,
        ),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListWheelScrollView(
        itemExtent: 164.0,
        squeeze: 0.9,
        perspective: 0.003,
        children: images.toList(),
      ),
    );
  }
}

Post Messages to Slack with Dart



import 'dart:convert' show utf8;
import 'dart:convert' show json;

class SlackMessage {
  final String? inChannel;
  final String? userName;
  final String message;
  final String? iconEmoji;

  const SlackMessage({
    required this.inChannel,
    required this.userName,
    required this.message,
    required this.iconEmoji,
  });

  Future<bool> send(String webhookUrl) async {
    final payload = {
      'text': message,
      if (inChannel != null) 'channel': inChannel!,
      if (userName != null) 'username': userName!,
      if (iconEmoji != null) 'icon_emoji': iconEmoji!
    };

    final request = await HttpClient().postUrl(Uri.parse(webhookUrl));
    final payloadData = utf8.encode(json.encode(payload));
    request.add(payloadData);
    final response = await request.close();
    return response.statusCode == 200;
  }
}

const webhookUrl = 'put your webhook url here';

void testIt() async {
  final message = SlackMessage(
    inChannel: 'dart',
    userName: 'Flutter',
    message: 'Hello from Dart in Terminal',
    iconEmoji: 'blue_heart:',
  );
  if (await message.send(webhookUrl)) {
    print('Successfully sent the message');
  } else {
    print('Could not send the message');
  }
}

Unwrap List<T?>? in Dart



extension Unwrap<T> on List<T?>? {
  List<T> unwrap() => (this ?? []).whereType<T>().toList();
}

void testOptionalListOfOptionals() {
  final List<int?>? optionalListOfOptionals = [1, 2, null, 3, null];
  final unwrapped = optionalListOfOptionals.unwrap(); // List<int>
  print(unwrapped); // prints [1, 2, 3]
}

void testListOfOptionals() {
  final listOfOptionals = [20, 30, null, 40]; // List<int?>
  final unwrapped = listOfOptionals.unwrap(); // List<int>
  print(unwrapped); // prints [20, 30, 40]
}

void testNormalList() {
  final list = [50, 60, 70]; // List<int>
  final unwrapped = list.unwrap(); // List<int>
  print(unwrapped); // prints [50, 60, 70]
}

Avoiding UI Jitters When Switching Widgets in Flutter



const imageUrls = [
  'https://cnn.it/3xu58Ap', // spring
  'https://bit.ly/2VcCSow', // summer
  'https://bit.ly/3A3zStC', // autumn
  'https://bit.ly/2TNY7wi' // winter
];

extension ToNetworkImage<T extends String> on List<T> {
  List<Widget> toNetworkImages() => map((s) => Image.network(s)).toList();
}

class HomePage extends StatefulWidget {
  @override
  State createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  var _currentIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Indexed Stack')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            IndexedStack(
              index: _currentIndex,
              children: imageUrls.toNetworkImages(),
            ),
            TextButton(
              onPressed: () {
                setState(
                  () {
                    _currentIndex++;
                    if (_currentIndex >= imageUrls.length) {
                      _currentIndex = 0;
                    }
                  },
                );
              },
              child: Text('Go to next season'),
            )
          ],
        ),
      ),
    );
  }
}

Detect Redirects in Dart



Future<bool> doesRedirect(String url) => HttpClient()
    .headUrl(Uri.parse(url))
    .then((req) => req..followRedirects = false)
    .then((req) => req.close())
    .then((resp) => resp.statusCode)
    .then((statusCode) => [301, 302, 303, 307, 308].contains(statusCode));

void testIt() async {
  final test1 = await doesRedirect('https://cnn.it/3xu58Ap');
  assert(test1 == true);

  final test2 = await doesRedirect('https://dart.dev');
  assert(test2 == false);

  final test3 = await doesRedirect('https://bit.ly/2VcCSow');
  assert(test3 == true);
}

Proportional Constraints in Flutter



class ProportionalWidthNetworkImage extends StatelessWidget {
  final String url;
  final double widthProportion;

  const ProportionalWidthNetworkImage(
      {Key? key, required this.url, required this.widthProportion})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        return Image.network(
          url,
          loadingBuilder: (context, child, loadingProgress) {
            final widget =
                loadingProgress == null ? child : LinearProgressIndicator();
            return Container(
              width: constraints.maxWidth * widthProportion,
              child: widget,
            );
          },
        );
      },
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ProportionalWidthNetworkImage(
          url: 'https://cnn.it/3xu58Ap',
          widthProportion: 0.8,
        ),
      ),
    );
  }
}

Displaying Cupertino Action Sheets in Flutter



import 'package:flutter/cupertino.dart';

enum Season { spring, summer, autumn, winter }

extension Title on Season {
  String get title => describeEnum(this).capitalized;
}

extension Caps on String {
  String get capitalized => this[0].toUpperCase() + substring(1);
}

extension ToWidget on Season {
  Widget toWidget() {
    switch (this) {
      case Season.spring:
        return Image.network('https://cnn.it/3xu58Ap');
      case Season.summer:
        return Image.network('https://bit.ly/2VcCSow');
      case Season.autumn:
        return Image.network('https://bit.ly/3A3zStC');
      case Season.winter:
        return Image.network('https://bit.ly/2TNY7wi');
    }
  }
}

class HomePage extends StatefulWidget {
  HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

Future<Season> _chooseSeason(
  BuildContext context,
  Season currentSeason,
) async {
  CupertinoActionSheet actionSheet(BuildContext context) {
    return CupertinoActionSheet(
      title: Text('Choose your favorite season:'),
      actions: Season.values
          .map(
            (season) => CupertinoActionSheetAction(
              onPressed: () {
                Navigator.of(context).pop(season);
              },
              child: Text(season.title),
            ),
          )
          .toList(),
      cancelButton: CupertinoActionSheetAction(
        onPressed: () {
          Navigator.of(context).pop(currentSeason);
        },
        child: Text('Cancel'),
      ),
    );
  }

  return await showCupertinoModalPopup(
    context: context,
    builder: (context) => actionSheet(context),
  );
}

class _HomePageState extends State<HomePage> {
  var _season = Season.spring;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(_season.title),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          _season.toWidget(),
          TextButton(
            onPressed: () async {
              _season = await _chooseSeason(
                context,
                _season,
              );
              setState(() {});
            },
            child: Text('Choose a season'),
          ),
        ],
      ),
    );
  }
}

Rotating List<T> in Dart



extension Rotate<T> on List<T> {
  int _rotationTimes(int places) {
    if (isEmpty) {
      return 0;
    }
    if (places == 0) {
      throw ArgumentError('places should be more than 0');
    }
    return places % length;
  }

  List<T> rotatedRight(int places) {
    final times = _rotationTimes(places);
    if (times == 0) {
      return this;
    } else {
      final cutOff = length - times;
      return sublist(cutOff)..addAll(sublist(0, cutOff));
    }
  }

  List<T> rotatedLeft(int places) {
    final times = _rotationTimes(places);
    if (times == 0) {
      return this;
    } else {
      return sublist(times)..addAll(sublist(0, times));
    }
  }
}

extension Equality<T extends Comparable> on List<T> {
  bool isEqualTo(List<T> other) {
    if (other.length != length) {
      return false;
    }
    for (var i = 0; i < length; i++) {
      if (other[i] != this[i]) {
        return false;
      }
    }
    return true;
  }
}

const arr = [1, 2, 3];

void testIt() {
  assert(arr.rotatedRight(1).isEqualTo([3, 1, 2]));
  assert(arr.rotatedRight(2).isEqualTo([2, 3, 1]));
  assert(arr.rotatedRight(3).isEqualTo([1, 2, 3]));
  assert(arr.rotatedRight(4).isEqualTo([3, 1, 2]));
  assert(arr.rotatedLeft(1).isEqualTo([2, 3, 1]));
  assert(arr.rotatedLeft(2).isEqualTo([3, 1, 2]));
  assert(arr.rotatedLeft(3).isEqualTo([1, 2, 3]));
  assert(arr.rotatedLeft(4).isEqualTo([2, 3, 1]));
}

Displaying SnackBars in Flutter



class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Hello world'),
      ),
      body: Center(
        child: TextButton(
          onPressed: () {
            final now = DateFormat('kk:mm:ss').format(DateTime.now());
            ScaffoldMessenger.of(context).removeCurrentSnackBar();
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(
                behavior: SnackBarBehavior.floating,
                elevation: 5.0,
                backgroundColor:
                    Colors.blue[600]!.withOpacity(0.8).withAlpha(200),
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(10),
                  side: BorderSide(
                    color: Colors.black.withOpacity(0.4),
                    width: 3.0,
                  ),
                ),
                content: Text('Some text $now'),
              ),
            );
          },
          child: Text('Show toast'),
        ),
      ),
    );
  }
}

Custom Tab Bar Using ToggleButtons in Flutter



class TabBarButton extends StatelessWidget {
  final IconData icon;
  final double size;

  const TabBarButton({Key? key, required this.icon, this.size = 60.0})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Icon(
        icon,
        size: size,
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Toggle Buttons'),
      ),
      body: Column(
        children: [
          CustomTabBar(),
        ],
      ),
    );
  }
}

class CustomTabBar extends StatefulWidget {
  const CustomTabBar({Key? key}) : super(key: key);

  @override
  _CustomTabBarState createState() => _CustomTabBarState();
}

class _CustomTabBarState extends State<CustomTabBar> {
  var _selection = [false, false, false];

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Align(
        alignment: FractionalOffset.bottomCenter,
        child: SafeArea(
          child: ToggleButtons(
            isSelected: _selection,
            onPressed: (index) {
              setState(() {
                _selection = List.generate(
                  _selection.length,
                  (i) => index == i ? true : false,
                );
              });
            },
            selectedColor: Colors.white,
            fillColor: Colors.blue,
            borderRadius: BorderRadius.circular(10.0),
            borderWidth: 4.0,
            borderColor: Colors.blue[400],
            selectedBorderColor: Colors.blue,
            highlightColor: Colors.blue.withOpacity(0.2),
            children: [
              TabBarButton(icon: Icons.settings),
              TabBarButton(icon: Icons.add),
              TabBarButton(icon: Icons.settings_remote)
            ],
          ),
        ),
      ),
    );
  }
}

Hashable Mixins in Dart



enum PetType { cat, dog }

mixin Pet {
  String get name;
  int get age;
  PetType get type;
  @override
  String toString() => 'Pet ($type), name = $name, age = $age';
  @override
  int get hashCode => hashValues(name, age, type);
  @override
  bool operator ==(covariant Pet o) => o.hashCode == hashCode;
}

class Cat with Pet {
  @override
  final String name;
  @override
  final int age;
  @override
  final PetType type;
  const Cat({required this.name, required this.age}) : type = PetType.cat;
}

void testIt() {
  final cats = <Cat>{
    Cat(name: 'Kitty 1', age: 2),
    Cat(name: 'Kitty 2', age: 3),
    Cat(name: 'Kitty 1', age: 2),
  };
  cats.forEach(print);
  /* 👆🏻 prints the following:
  Pet (PetType.cat), name = Kitty 1, age = 2
  Pet (PetType.cat), name = Kitty 2, age = 3
  */
}

Flutter Tips and Tricks in Terminal



import 'dart:convert' show utf8;
import 'dart:io' show HttpClient, exit, Process, stderr;
import 'dart:math' show Random;

const rawBlobRoot =
    'https://raw.githubusercontent.com/vandadnp/flutter-tips-and-tricks/main/';

void main(List<String> args) async {
  final url = Uri.https('bit.ly', '/2V1GKsC');
  try {
    final client = HttpClient();
    final images = await client
        .getUrl(url)
        .then((req) => req.close())
        .then((resp) => resp.transform(utf8.decoder).join())
        .then((body) => body.split('\n').map((e) => e.trim()))
        .then((iter) => iter.toList())
        .then((list) => list..retainWhere((s) => s.endsWith('.jpg)')))
        .then((imageList) => imageList.map((e) => e.replaceAll('![](', '')))
        .then((imageList) => imageList.map((e) => e.replaceAll(')', '')))
        .then((iter) => iter.toList());

    final found = images[Random().nextInt(images.length)];
    final result = '$rawBlobRoot$found';
    await Process.run('open', [result]);
    exit(0);
  } catch (e) {
    stderr.writeln('Could not proceed due to $e');
    exit(1);
  }
}

Searching List<List<T>> in Dart



const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const arr3 = [7, 8, 9];
const arr = [arr1, arr2, arr3];

extension FlattenFind<T extends Comparable> on Iterable<Iterable<T>> {
  bool containsElement(T value) {
    for (final arr in this) {
      if (arr.contains(value)) {
        return true;
      }
    }
    return false;
  }
}

void testIt() {
  assert(arr.containsElement(2));
  assert(arr.containsElement(8));
  assert(!arr.containsElement(10));
  assert(!arr.containsElement(10));
}

Cloning Objects in Dart



class Person {
  final Map<String, Object> _values;
  static const FIRST_NAME_KEY = 'FIRST_NAME';
  static const LAST_NAME_KEY = 'LAST_NAME';

  Person.from(Map<String, Object> props) : _values = props;

  Person({
    required String firstName,
    required String lastName,
    Map<String, Object>? props,
  }) : _values = {
          FIRST_NAME_KEY: firstName,
          LAST_NAME_KEY: lastName,
        };

  @override
  bool operator ==(covariant Person other) =>
      other.firstName == firstName && other.lastName == lastName;

  @override
  String toString() => _values.toString();
}

extension Properties on Person {
  String get firstName => _values[Person.FIRST_NAME_KEY].toString();
  set firstName(String newValue) => _values[Person.FIRST_NAME_KEY] = newValue;

  String get lastName => _values[Person.LAST_NAME_KEY].toString();
  set lastName(String newValue) => _values[Person.LAST_NAME_KEY] = newValue;
}

extension Clone on Person {
  Person clone([Map<String, Object>? additionalProps]) =>
      Person.from(Map.from(_values)..addAll(additionalProps ?? {}));
}

extension Subscripts on Person {
  Object? operator [](String key) => _values[key];
  operator []=(String key, Object value) => _values[key] = value;
}

void testIt() {
  final foo = Person(
    firstName: 'Foo Firstname',
    lastName: 'Foo Lastname',
  );
  print(foo); // {FIRST_NAME: Foo Firstname, LAST_NAME: Foo Lastname}
  final copyOfFoo = foo.clone();
  print(copyOfFoo); // {FIRST_NAME: Foo Firstname, LAST_NAME: Foo Lastname}
  final bar = foo.clone({'age': 30});
  print(bar); // {FIRST_NAME: Foo Firstname, LAST_NAME: Foo Lastname, age: 30}
  assert(foo == copyOfFoo);
  assert(foo == bar);
  assert(foo['age'] == null);
  assert(copyOfFoo['age'] == null);
  assert(bar['age'] == 30);
}

Color Filters in Flutter



class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  var sliderValue = 0.0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Color Filters in Flutter!'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ColorFiltered(
            colorFilter: ColorFilter.mode(
              Colors.orange.withOpacity(sliderValue),
              BlendMode.colorBurn,
            ),
            child: Image.network('https://tinyurl.com/4vtvh35h'),
          ),
          Slider(
            value: sliderValue,
            onChanged: (value) {
              setState(() {
                sliderValue = value;
              });
            },
          )
        ],
      ),
    );
  }
}

Flattening Lists in Dart



class Person {
  final String name;
  const Person(this.name);
  @override
  String toString() => 'Person: $name';
}

class House {
  final List<Person>? tennants;
  final List<Person> builders;
  const House({
    required this.tennants,
    required this.builders,
  });
}

const houses = [
  House(tennants: null, builders: [
    Person('Builder 1'),
  ]),
  House(tennants: [
    Person('Tennant 1'),
    Person('Tennant 2'),
  ], builders: [
    Person('Builder 3')
  ]),
];

extension OptionalFlattend<T> on Iterable<List<T>?> {
  Iterable<T> flattened() => expand((e) => e ?? []);
}

void testOptionalFlatten() {
  final allTennants = houses.map((h) => h.tennants).flattened();
  print(allTennants); // Person: Tennant 1, Person: Tennant 2
}

extension Flattend<T> on Iterable<List<T>> {
  Iterable<T> flattened() => expand((e) => e);
}

void testNonOptionalFlatten() {
  final allBuilders = houses.map((h) => h.builders).flattened();
  print(allBuilders); // Person: Builder 1, Person: Builder 2
}

void testIt() {
  testOptionalFlatten();
  testNonOptionalFlatten();
}

Managing Duplicates in List<T> in Dart



extension Duplicates<T> on List<T> {
  void addAllByAvoidingDuplicates(Iterable<T> values) =>
      replaceRange(0, length, {
        ...([...this] + [...values])
      });

  int get numberOfDuplicates => length - {...this}.length;

  bool get containsDuplicates => numberOfDuplicates > 0;

  List<T> get uniques => [
        ...{...this}
      ];

  void removeDuplicates() => replaceRange(
        0,
        length,
        uniques,
      );

  List<T> get duplicates => [
        for (var i = 0; i < length; i++)
          [...this].skip(i + 1).contains(this[i]) ? this[i] : null
      ].whereType<T>().toList();
}

void testIt() {
  final values = [3, 2, 10, 30, 40, 30, 100, 10];

  assert(values.numberOfDuplicates == 2);
  assert(values.containsDuplicates == true);

  assert(values.uniques.length == values.length - 2);
  print(values.uniques); // [3, 2, 10, 30, 40, 100]

  values.removeDuplicates();
  print(values); // [3, 2, 10, 30, 40, 100]

  assert(values.numberOfDuplicates == 0);
  assert(!values.containsDuplicates);
  assert(values.duplicates.isEmpty);

  values.addAllByAvoidingDuplicates([3, 2, 10, 200]);
  print(values); // [3, 2, 10, 30, 40, 100, 200]
  assert(values.containsDuplicates == false);
}

FlatMap and CompactMap in Dart



extension CompactMap<T> on List<T> {
  List<E> compactMap<E>(E? Function(T element) f) {
    Iterable<E> imp(E? Function(T element) f) sync* {
      for (final value in this) {
        final mapped = f(value);
        if (mapped != null) {
          yield mapped;
        }
      }
    }

    return imp(f).toList();
  }
}

extension FlatMap<T> on T? {
  E? flatMap<E>(E? Function(T value) f) {
    if (this != null) {
      return f(this!);
    } else {
      return null;
    }
  }
}

void testIt() {
  final foo = [1, 2, null, 3, null, 4];
  final bar = foo.compactMap((element) => element.flatMap((e) => e * 2));
  print(bar); // prints 2, 4, 6, 8
}

Equality of List<T> in Dart



extension Equality<T extends Comparable> on List<T> {
  bool isEqualTo(List<T> other) {
    if (other.length != length) {
      return false;
    }
    for (var i = 0; i < length; i++) {
      if (other[i] != this[i]) {
        return false;
      }
    }
    return true;
  }
}

int ascendingComparator<T extends Comparable>(T lhs, T rhs) =>
    lhs.compareTo(rhs);
int descendingComparator<T extends Comparable>(T lhs, T rhs) =>
    rhs.compareTo(lhs);

extension Sorted<T extends Comparable> on List<T> {
  List<T> sorted({bool descending = false}) => descending
      ? ([...this]..sort(descendingComparator))
      : ([...this]..sort(ascendingComparator));
}

void testIt() {
  assert([1, 2, 3].isEqualTo([1, 2, 3]));
  assert(![1, 2, 3].isEqualTo([1, 2, 2]));
  assert([3, 1, 2].sorted().isEqualTo([1, 2, 3]));
  assert(![3, 1, 2].sorted().isEqualTo([3, 1, 2]));
  assert(['Foo', 'Bar', 'Baz'].isEqualTo(['Foo', 'Bar', 'Baz']));
  assert(!['Foo', 'Bar', 'Baz'].isEqualTo(['foo', 'Bar', 'Baz']));
}

Constants in Dart




class Person {
  final String name;
  final int age;
  const Person({required this.name, required this.age});
}

const foo = Person(name: 'Foo', age: 20);
const foo2 = Person(name: 'Foo', age: 20);
const bar = Person(name: 'Bar', age: 20);

void assert_eq(Object lhs, Object rhs) {
  assert(lhs == rhs);
}

void assert_ne(Object lhs, Object rhs) {
  assert(lhs != rhs);
}

void testIt() {
  assert_eq(foo, foo2);
  assert_ne(foo, bar);
  assert_ne(foo2, bar);
}

Displaying Scrollable Bottom Sheets in Flutter



class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Scrollable Sheet')),
      body: DraggableScrollableSheet(
        initialChildSize: 0.2,
        minChildSize: 0.2,
        maxChildSize: 0.8,
        builder: (context, scrollController) {
          return Container(
            decoration: decoration(),
            clipBehavior: Clip.antiAlias,
            child: SingleChildScrollView(
              controller: scrollController,
              child: column(),
            ),
          );
        },
      ),
    );
  }
}

const urls = [
  'https://tinyurl.com/4vtvh35h',
  'https://tinyurl.com/pujhs55w',
  'https://tinyurl.com/u5k7zueh',
];

List<Widget> imageWithLoremIpsum(String uri) => [
      Image.network(uri),
      SizedBox(height: 10),
      loremIpsum(),
      SizedBox(height: 10),
    ];

Column column() => Column(
      children: imageWithLoremIpsum(urls[0]) +
          imageWithLoremIpsum(urls[1]) +
          imageWithLoremIpsum(urls[2]),
    );

Text loremIpsum() => Text(
      'Lorem ipsum ' * 10,
      textAlign: TextAlign.center,
    );

BoxDecoration decoration() => BoxDecoration(
      border: Border.all(color: Colors.white),
      borderRadius: BorderRadius.only(
        topLeft: Radius.circular(10),
        topRight: Radius.circular(10),
      ),
      color: Colors.white70,
    );

YouTube Ad Remover in Dart



import 'dart:io' show stdout, stderr, exitCode;
import 'package:collection/collection.dart' show IterableExtension;

// example argument: https://www.youtube.com/watch?v=mtETXtSP0pA
void main(List<String> args) async {
  if (args.isEmpty) {
    stdout.writeln('usage: dart youtube.dart "https://..."');
    return;
  }

  final link =
      args.firstWhereOrNull((element) => Uri.tryParse(element) != null);

  if (link == null) {
    stderr.writeln('No YouTube url found');
    exitCode = 1;
    return;
  }

  try {
    final uri = Uri.parse(link);
    if (uri.scheme.toLowerCase() != 'https' ||
        uri.host.toLowerCase() != 'www.youtube.com' ||
        uri.queryParameters['v'] == null) {
      throw FormatException();
    } else {
      final videoId = uri.queryParameters['v'];
      final embedUri = Uri.parse('${uri.scheme}://${uri.host}/embed/$videoId');
      stdout.writeln(embedUri);
      exitCode = 0;
    }
  } on FormatException catch (e) {
    stderr.writeln('Invalid Uri, try again! err = $e');
    exitCode = 1;
    return;
  }
}

Fade Between Widgets in Flutter



const urls = [
  'https://bit.ly/3qYOtDm',
  'https://bit.ly/3wt11Ec',
];

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  var isShowingFirstImage = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('AnimatedCrossFade in Flutter'),
      ),
      body: Center(
        child: AnimatedCrossFade(
          layoutBuilder: (topChild, topChildKey, bottomChild, bottomChildKey) {
            return GestureDetector(
              onTap: () {
                setState(() {
                  isShowingFirstImage = !isShowingFirstImage;
                });
              },
              child: AnimatedCrossFade.defaultLayoutBuilder(
                  topChild, topChildKey, bottomChild, bottomChildKey),
            );
          },
          firstChild: Image.network(urls[0]),
          secondChild: Image.network(urls[1]),
          crossFadeState: isShowingFirstImage
              ? CrossFadeState.showFirst
              : CrossFadeState.showSecond,
          duration: Duration(milliseconds: 400),
        ),
      ),
    );
  }
}

Sort Descriptors in Dart



int ascendingComparator<T extends Comparable>(T lhs, T rhs) =>
    lhs.compareTo(rhs);
int descendingComparator<T extends Comparable>(T lhs, T rhs) =>
    rhs.compareTo(lhs);

extension Sorted<T extends Comparable> on List<T> {
  List<T> sorted({bool descending = false}) => descending
      ? (this..sort(descendingComparator))
      : (this..sort(ascendingComparator));
}

class Person implements Comparable {
  final int age;
  final String name;
  const Person({required this.age, required this.name});
  @override
  int compareTo(covariant Person other) => age.compareTo(other.age);
  @override
  String toString() => 'Person, name = $name ($age)';
}

void testIt() {
  final people = [
    Person(age: 39, name: 'Father Foo'),
    Person(age: 40, name: 'Mother Bar'),
    Person(age: 13, name: 'Son Baz'),
  ];

  print('ascending sort');
  people.sorted().forEach(print);
  // prints Son Baz (13), Father Foo (39), Mother Bar (40)
  print('descending sort');
  people.sorted(descending: true).forEach(print);
  // prints Mother Bar (40), Father Foo (39), Son Baz (13)
}

User Sortable Columns and Tables in Flutter



class Language {
  final String name;
  final Image image;
  const Language(this.name, this.image);
  Language.dart()
      : name = 'Dart',
        image = Image.network('https://bit.ly/3yH1Ivj');
  Language.rust()
      : name = 'Rust',
        image = Image.network('https://bit.ly/3lPTqhb');
  Language.python()
      : name = 'Python',
        image = Image.network('https://bit.ly/3iCFCEP');

  Language.java()
      : name = 'Java',
        image = Image.network('https://bit.ly/3CCapJH');
  static List<Language> all = [
    Language.dart(),
    Language.rust(),
    Language.python(),
    Language.java(),
  ];
}

extension Sort on List<Language> {
  void sortByName(bool ascending) => sort((lhs, rhs) =>
      ascending ? lhs.name.compareTo(rhs.name) : rhs.name.compareTo(lhs.name));
}

List<DataRow> rows(List<Language> langs) => langs
    .map(
      (l) => DataRow(
        cells: [
          DataCell(
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: l.image,
            ),
          ),
          DataCell(Text(l.name)),
        ],
      ),
    )
    .toList();

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final List<Language> _langs = Language.all..sortByName(true);
  int sortedColumnIndex = 1;
  var isSortedAscending = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('WhatsApp')),
      body: DataTable(
        sortAscending: isSortedAscending,
        sortColumnIndex: sortedColumnIndex,
        columns: [
          DataColumn(label: Text('Image')),
          DataColumn(
            label: Text('Name'),
            onSort: (columnIndex, ascending) {
              setState(() {
                sortedColumnIndex = columnIndex;
                isSortedAscending = ascending;
                _langs.sortByName(ascending);
              });
            },
          ),
        ],
        rows: rows(_langs),
      ),
    );
  }
}

Content-Length of List<Uri> in Dart

Recursive Dot Notation on Maps in Dart




final person = {
  'firstName': 'Foo',
  'lastName': 'Bar',
  'age': 30,
  'address': {
    'street': {
      'name': 'Baz street',
      'numberOfHouses': 20,
    },
    'houseNumber': '#20',
    'city': 'Stockholm',
    'country': 'Sweden'
  },
};

extension KeyPath on Map {
  Object? valueFor({required String keyPath}) {
    final keysSplit = keyPath.split('.');
    final thisKey = keysSplit.removeAt(0);
    final thisValue = this[thisKey];
    if (keysSplit.isEmpty) {
      return thisValue;
    } else if (thisValue is Map) {
      return thisValue.valueFor(keyPath: keysSplit.join('.'));
    }
  }
}

void testIt() {
  assert(person.valueFor(keyPath: 'firstName') == 'Foo');
  assert(person.valueFor(keyPath: 'age') == 30);
  assert(person.valueFor(keyPath: 'address.street.name') == 'Baz street');
  assert(person.valueFor(keyPath: 'address.houseNumber') == '#20');
}

Allow User Selection of Text in Flutter



const text = 'Flutter is an open-source UI software development'
    ' kit created by Google. It is used to develop cross platform applications'
    ' for Android, iOS, Linux, Mac, Windows, Google Fuchsia, '
    'and the web from a single codebase.';

const imageUrl = 'https://bit.ly/3gT5Qk2';

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Selectable Text in Flutter'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Image.network(imageUrl),
          SizedBox(height: 10.0),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: SelectableText(
              text,
              textAlign: TextAlign.center,
              showCursor: true,
              cursorColor: Colors.blue,
              toolbarOptions: ToolbarOptions(
                copy: true,
                selectAll: true,
              ),
              style: TextStyle(
                fontSize: 20,
                fontWeight: FontWeight.w300,
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Placing Constraints on Widgets in Flutter



const dashes = [
  'https://bit.ly/3gHlTCU',
  'https://bit.ly/3wOLO1c',
  'https://bit.ly/3cXWD9j',
  'https://bit.ly/3gT5Qk2',
];

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('ConstrainedBox in Flutter'),
      ),
      body: InteractiveViewer(
        minScale: 1.0,
        maxScale: 2.0,
        child: SingleChildScrollView(
          scrollDirection: Axis.vertical,
          child: Table(
            defaultVerticalAlignment: TableCellVerticalAlignment.middle,
            children: dashes
                .map(
                  (dash) => TableRow(
                    children: [
                      ConstrainedBox(
                        constraints: BoxConstraints(
                          minHeight: 300,
                        ),
                        child: Image.network(dash),
                      ),
                    ],
                  ),
                )
                .toList(),
          ),
        ),
      ),
    );
  }
}

Animating Position Changes in Flutter



class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  var isMovedUp = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('AnimatedPositioned in Flutter')),
      body: Center(
        child: GestureDetector(
          onTap: () => setState(() => isMovedUp = !isMovedUp),
          child: Stack(
            clipBehavior: Clip.none,
            alignment: Alignment.center,
            children: [
              Image.network('https://bit.ly/2VcCSow'),
              Text(
                'Summer 😎',
                style: TextStyle(
                  fontSize: 30,
                  color: Colors.black,
                ),
              ),
              AnimatedPositioned(
                duration: Duration(seconds: 1),
                bottom: isMovedUp ? 140 : 10.0,
                curve: Curves.elasticInOut,
                child: CircleAvatar(
                  radius: 100,
                  backgroundImage: NetworkImage('https://bit.ly/3cXWD9j'),
                  backgroundColor: Colors.orange[300],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Transitioning Between Widgets in Flutter



enum Season { spring, summer, autumn, winter }

extension Caps on String {
  String get capitalized => this[0].toUpperCase() + substring(1);
}

extension Title on Season {
  String get title => describeEnum(this).capitalized;
}

class TitledImage {
  final String title;
  final Uri uri;
  final ValueKey key;
  const TitledImage(this.title, this.uri, this.key);

  TitledImage.spring()
      : title = Season.spring.title,
        uri = Uri.https('cnn.it', '/3xu58Ap'),
        key = ValueKey(1);

  TitledImage.summer()
      : title = Season.summer.title,
        uri = Uri.https('bit.ly', '/2VcCSow'),
        key = ValueKey(2);

  TitledImage.autumn()
      : title = Season.autumn.title,
        uri = Uri.https('bit.ly', '/3A3zStC'),
        key = ValueKey(3);

  TitledImage.winter()
      : title = Season.winter.title,
        uri = Uri.https('bit.ly', '/2TNY7wi'),
        key = ValueKey(4);
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  var _img = TitledImage.summer();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(_img.title)),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          AnimatedSwitcher(
            switchInCurve: Curves.easeIn,
            switchOutCurve: Curves.easeOut,
            duration: Duration(milliseconds: 300),
            transitionBuilder: (child, animation) {
              return FadeTransition(opacity: animation, child: child);
            },
            child: Image.network(
              _img.uri.toString(),
              key: _img.key,
            ),
          ),
          getButtons(),
        ],
      ),
    );
  }

  Widget getButtons() {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        TextButton(
          onPressed: () => setState(() => _img = TitledImage.spring()),
          child: Text(Season.spring.title),
        ),
        TextButton(
          onPressed: () => setState(() => _img = TitledImage.summer()),
          child: Text(Season.summer.title),
        ),
        TextButton(
          onPressed: () => setState(() => _img = TitledImage.autumn()),
          child: Text(Season.autumn.title),
        ),
        TextButton(
          onPressed: () => setState(() => _img = TitledImage.winter()),
          child: Text(Season.winter.title),
        ),
      ],
    );
  }
}

Doubly Linked Lists in Dart



class Person extends LinkedListEntry<Person> {
  final String name;
  final int age;
  Person({
    required this.name,
    required this.age,
  });

  @override
  String toString() => 'Person name = $name, age = $age';
}

void testIt() {
  final persons = LinkedList<Person>();
  final dad = Person(name: 'Father Foo', age: 47);
  final mom = Person(name: 'Mother Bar', age: 47);
  final daughter = Person(name: 'Daughter Baz', age: 22);
  persons.addAll([dad, mom, daughter]);

  print(persons.first.previous); // null
  print(persons.first); // Person name = Father Foo, age = 47
  print(persons.first.next); // Person name = Mother Bar, age = 47
  print(persons.last.previous); // Person name = Mother Bar, age = 47
  print(persons.first.next?.next); // Person name = Daughter Baz, age = 22
  print(persons.last.next); // null
}

Reordering Items Inside List Views in Flutter



class Item {
  final Color color;
  final String text;
  final UniqueKey uniqueKey;
  Item(this.color, this.text) : uniqueKey = UniqueKey();
}

extension ToListItem on Item {
  Widget toListItem() => LimitedBox(
        key: uniqueKey,
        maxHeight: 200,
        child: Container(
          color: color,
          child: Padding(
            padding: const EdgeInsets.all(20),
            child: Text(
              text,
              style: TextStyle(
                color: Colors.white,
                fontSize: 100,
              ),
            ),
          ),
        ),
      );
}

class _HomePageState extends State<HomePage> {
  var items = [
    Item(Colors.deepPurple, 'Foo'),
    Item(Colors.blueGrey, 'Bar'),
    Item(Colors.lightGreen, 'Baz')
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Reordered List View in Flutter'),
      ),
      body: ReorderableListView(
        onReorder: (oldIndex, newIndex) {
          setState(() {
            final item = items.removeAt(oldIndex);
            items.insert(newIndex, item);
          });
        },
        children: items.map((i) => i.toListItem()).toList(),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

Custom Stream Transformers in Dart



in this example we have created our own string transformer that
can trim a Stream<String> by trimming whitespace from both
beginning and end of the string
*/

import 'dart:convert' show utf8;

class StringTrimmer extends StreamTransformerBase<String, String> {
  const StringTrimmer();
  @override
  Stream<String> bind(Stream<String> stream) =>
      Stream.fromFuture(stream.join(' ')).map((str) => str.trim());
}

final string =
    '''   A long line of text with spaces in the beginning and the end, 
    divided into three lines just for the purpose of this demonstration    ''';

void testIt() async {
  final bytes = utf8.encode(string);
  final result = await Stream.value(bytes)
      .transform(utf8.decoder)
      .transform(LineSplitter())
      .transform(StringTrimmer())
      .join();
  print(result);
}

Expanding Stream Elements in Dart

/*


in this example we expand every element inside our Stream<int> to
a stream that in turn contains n+1 elements where n is the index generated
by our main stream, that's to say, 0, 1, 2, 3, 4, etc

*/
Stream<int> nestedEvents(int count) {
  return Stream.periodic(
    Duration(seconds: 1),
    (e) => e,
  ).take(count).asyncExpand(
        (i) => Stream.fromIterable(
          Iterable.generate(i + 1),
        ),
      );
}

void testIt() async {
  /* 
  prints the followings in this order
  0, 1
  0, 1, 2
  0, 1, 2, 3
  0, 1, 2, 3, 4
  */
  await for (final value in nestedEvents(5)) {
    print('Value is $value');
  }
}

Consume Streams for a Duration in Dart

extension TakeFor<T> on Stream<T> {
  Stream<T> takeFor(Duration duration) {
    final upTo = DateTime.now().add(duration);
    return takeWhile((_) {
      final now = DateTime.now();
      return now.isBefore(upTo) | now.isAtSameMomentAs(upTo);
    });
  }
}

Stream<DateTime> source() => Stream.periodic(
      Duration(milliseconds: 500),
      (_) => DateTime.now(),
    );

void testIt() async {
  await for (final dateTime in source().takeFor(
    Duration(seconds: 4),
  )) {
    print('date time is $dateTime');
  }
}

Shortening URLs in Dart

import 'dart:convert' show json;

Future<Uri> shortenUri(Uri uri, String bitlyToken) async {
  final client = HttpClient();

  final endpoint = Uri.https('api-ssl.bitly.com', '/v4/shorten');

  final response = await client.postUrl(endpoint).then(
    (req) {

      req.headers
        ..set(HttpHeaders.contentTypeHeader, 'application/json')
        ..set(HttpHeaders.authorizationHeader, 'Bearer $bitlyToken');

      final body = {
        'long_url': uri.toString(),
        'domain': 'bit.ly',
      };
      final bodyBytes = utf8.encode(json.encode(body));
      req.add(bodyBytes);

      return req.close();
    },
  );

  final responseBody = await response.transform(utf8.decoder).join();
  final responseJson = json.decode(responseBody) as Map<String, dynamic>;
  return Uri.parse(responseJson['link']);
}

void testIt() async {
  print(await shortenUri(
    Uri.parse('https://pixolity.se'),
    'XXX',
  ));
}

LimitedBox Widget as ListView Items in Flutter

const images = [
  'https://bit.ly/3x7J5Qt',
  'https://bit.ly/3ywI8l6',
  'https://bit.ly/3jRSRCu',
  'https://bit.ly/36fNNj9',
  'https://bit.ly/3jOueGG',
  'https://bit.ly/3qYOtDm',
  'https://bit.ly/3wt11Ec',
  'https://bit.ly/3yvFg7X',
  'https://bit.ly/3ywzOla',
  'https://bit.ly/3wnASpW',
  'https://bit.ly/3jXSDto',
];

extension ToListItemImage on String {
  Widget toListItemImage() {
    return LimitedBox(
      maxHeight: 150.0,
      child: Image.network(
        this,
        fit: BoxFit.fitWidth,
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Limited Box in Flutter')),
      body: ListView(
        children: images.map((str) => str.toListItemImage()).toList(),
      ),
    );
  }
}

Generically Convert Anything to Int in Dart

extension ToInt on Object {
  int toInt() {
    final list = [
      if (this is Iterable<Object>)
        ...(List.of(this as Iterable<Object>))
      else if (this is int)
        [this as int]
      else
        (double.tryParse(toString()) ?? 0.0).round()
    ];
    return list
        .map((e) => (double.tryParse(e.toString()) ?? 0.0).round())
        .reduce((lhs, rhs) => lhs + rhs);
  }
}

void testIt() {
  assert(1.toInt() == 1);
  assert((2.2).toInt() == 2);
  assert((2.0).toInt() == 2);
  assert('3'.toInt() == 3);
  assert(['4', '5'].toInt() == 9);
  assert([4, 5].toInt() == 9);
  assert(['2.4', '3.5'].toInt() == 6);
  assert(['2', '3.5'].toInt() == 6);
  assert({'2', 3, '4.2'}.toInt() == 9);
  assert(['2', 3, '4.2', 5.3].toInt() == 14);
}

Validating URL Certificates in Dart

import 'dart:io' show HttpClient;

Future<bool> isSecuredWithValidCert(String uriString) async {
  final uri = Uri.parse(uriString);
  final client = HttpClient();
  try {
    await client.headUrl(uri).then((r) => r.close());
    return true;
  } on HandshakeException {
    return false;
  }
}

void testIt() async {
  await isSecuredWithValidCert('https://expired.badssl.com');
  await isSecuredWithValidCert('https://wrong.host.badssl.com');
  await isSecuredWithValidCert('https://self-signed.badssl.com');
  await isSecuredWithValidCert('https://untrusted-root.badssl.com');
  await isSecuredWithValidCert('https://revoked.badssl.com');
}

Displaying Popup Menus in Flutter

enum ImageAction { copy }

PopupMenuItem<ImageAction> copyButton({VoidCallback? onPressed}) =>
    PopupMenuItem<ImageAction>(
      value: ImageAction.copy,
      child: TextButton.icon(
        icon: Icon(Icons.copy),
        label: Text('Copy'),
        onPressed: onPressed,
      ),
    );

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter'),
      ),
      body: Center(
        child: PopupMenuButton<ImageAction>(
          elevation: 10,
          offset: Offset(0, 50),
          itemBuilder: (_) => [
            copyButton(
              onPressed: () {
                print('Copy the image...');
              },
            ),
          ],
          child: Image.network('https://bit.ly/3ywI8l6'),
        ),
      ),
    );
  }
}

Implementing Drag and Drop in Flutter

class HomePage extends StatefulWidget {
  HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  String? _imageUrl;

  bool shouldAccept(String? value) => Uri.tryParse(value ?? '') != null;

  Widget dragTargetBuilder(
    BuildContext context,
    List<String?> incoming,
    dynamic rejected,
  ) {
    final emptyContainer = Container(
      color: Colors.grey[200],
      height: 200,
      child: Center(
        child: Text('Drag an image here'),
      ),
    );

    if (incoming.isNotEmpty) {
      _imageUrl = incoming.first;
    }

    if (_imageUrl == null) {
      return emptyContainer;
    }

    try {
      final uri = Uri.parse(_imageUrl ?? '');
      return Container(
        color: Colors.grey[200],
        height: 200,
        child: Center(
          child: Image.network(uri.toString()),
        ),
      );
    } on FormatException {
      return emptyContainer;
    }
  }

  static final firstImageUrl = 'https://bit.ly/3xnoJTm';
  static final secondImageUrl = 'https://bit.ly/3hIuC78';
  final firstImage = Image.network(firstImageUrl);
  final secondImage = Image.network(secondImageUrl);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Tooltips in Flutter')),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          children: [
            DragTarget<String>(
              onWillAccept: shouldAccept,
              builder: dragTargetBuilder,
            ),
            SizedBox(height: 10.0),
            DraggableImage(
              imageWidget: firstImage,
              imageUrl: firstImageUrl,
            ),
            SizedBox(height: 10.0),
            DraggableImage(
              imageWidget: secondImage,
              imageUrl: secondImageUrl,
            ),
          ],
        ),
      ),
    );
  }
}

class DraggableImage extends StatelessWidget {
  const DraggableImage({
    Key? key,
    required this.imageWidget,
    required this.imageUrl,
  }) : super(key: key);

  final Image imageWidget;
  final String imageUrl;

  @override
  Widget build(BuildContext context) {
    return Draggable<String>(
      data: imageUrl,
      feedback: Container(
        width: MediaQuery.of(context).size.width,
        decoration: BoxDecoration(
          boxShadow: [
            BoxShadow(
              blurRadius: 30,
              color: Colors.black,
              spreadRadius: 10,
            ),
          ],
        ),
        child: imageWidget,
      ),
      child: imageWidget,
    );
  }
}

Dismissing List Items in Flutter

const gridImages = [
  'https://bit.ly/3x7J5Qt',
  'https://bit.ly/3dLJNeD',
  'https://bit.ly/3ywI8l6',
  'https://bit.ly/3jRSRCu',
  'https://bit.ly/36fNNj9',
  'https://bit.ly/3jOueGG',
  'https://bit.ly/3qYOtDm',
  'https://bit.ly/3wt11Ec',
  'https://bit.ly/3yvFg7X',
  'https://bit.ly/3ywzOla',
  'https://bit.ly/3wnASpW',
  'https://bit.ly/3jXSDto',
];

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: [
          CustomAppBar(),
          CustomGridView(),
          CustomListView(
            imageUrls: gridImages,
          ),
        ],
      ),
    );
  }
}

class _CustomListViewState extends State<CustomListView> {
  @override
  Widget build(BuildContext context) {
    return SliverPadding(
      padding: EdgeInsets.all(8.0),
      sliver: SliverList(
        delegate: SliverChildBuilderDelegate(
          (context, index) {
            final url = widget.imageUrls[index];
            return Dismissible(
              key: ValueKey(url),
              onDismissed: (_) {
                widget.imageUrls.remove(url);
              },
              background: Container(
                color: Colors.red,
                child: FittedBox(
                  alignment: Alignment.centerRight,
                  fit: BoxFit.fitHeight,
                  child: Icon(Icons.delete, color: Colors.white),
                ),
              ),
              child: Padding(
                padding: const EdgeInsets.only(bottom: 8.0),
                child: Image.network(url),
              ),
            );
          },
          childCount: widget.imageUrls.length,
        ),
      ),
    );
  }
}

class CustomListView extends StatefulWidget {
  final List<String> imageUrls;

  const CustomListView({
    Key? key,
    required this.imageUrls,
  }) : super(key: key);

  @override
  _CustomListViewState createState() => _CustomListViewState();
}

class CustomGridView extends StatelessWidget {
  const CustomGridView({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SliverPadding(
      padding: EdgeInsets.all(8.0),
      sliver: SliverGrid(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 3,
          mainAxisSpacing: 10,
          crossAxisSpacing: 10,
          childAspectRatio: 1.0,
        ),
        delegate: SliverChildBuilderDelegate(
          (context, index) {
            return Container(
              width: 100,
              height: 100,
              child: Image.network(gridImages[index]),
            );
          },
          childCount: gridImages.length,
        ),
      ),
    );
  }
}

class CustomAppBar extends StatelessWidget {
  const CustomAppBar({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SliverAppBar(
      backgroundColor: Colors.orange[300],
      forceElevated: true,
      pinned: false,
      snap: false,
      floating: true,
      expandedHeight: 172,
      flexibleSpace: FlexibleSpaceBar(
        title: Text(
          'Flutter',
          style: TextStyle(
            fontSize: 30,
            color: Colors.white,
            decoration: TextDecoration.underline,
          ),
        ),
        collapseMode: CollapseMode.parallax,
        background: Image.network('https://bit.ly/3x7J5Qt'),
      ),
    );
  }
}

Animating Widgets with Ease in Flutter

class Ball extends StatefulWidget {
  const Ball({Key? key}) : super(key: key);

  @override
  _BallState createState() => _BallState();
}

class _BallState extends State<Ball> with SingleTickerProviderStateMixin {
  late final AnimationController _controller;
  late final Animation _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 4),
      reverseDuration: Duration(seconds: 4),
    );
    _animation = Tween(begin: 0.0, end: 2 * pi).animate(_controller);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    _controller.repeat();
    return AnimatedBuilder(
      animation: _animation,
      builder: (context, image) {
        return Transform.rotate(
          angle: _animation.value,
          child: image,
        );
      },
      child: Image.network('https://bit.ly/3xspdrp'),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Animated Builder in Flutter'),
      ),
      body: Center(
        child: Ball(),
      ),
    );
  }
}

Displaying Tool Tips in Flutter

const imagesAndInfo = [
  ['https://bit.ly/3xnoJTm', 'Stockholm, Sweden'],
  ['https://bit.ly/3hIuC78', 'Dalarna, Sweden'],
  ['https://bit.ly/3wi9mdG', 'Brighton, UK'],
  ['https://bit.ly/3dSSMuy', 'Hove, UK'],
  ['https://bit.ly/3xoWCmV', 'Kerala, India'],
  ['https://bit.ly/3hGmjZC', 'Salvador da Bahia, Brazil']
];

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Tooltips in Flutter')),
      body: ListView.builder(
        itemCount: imagesAndInfo.length,
        itemBuilder: (_, index) {
          return Padding(
            padding: const EdgeInsets.only(top: 8.0, left: 8.0, right: 8.0),
            child: Tooltip(
              decoration: BoxDecoration(
                color: Colors.black,
                boxShadow: [
                  BoxShadow(
                    color: Colors.white.withAlpha(180),
                    offset: Offset.zero,
                    spreadRadius: 30.0,
                    blurRadius: 30.0,
                  ),
                ],
                borderRadius: BorderRadius.all(Radius.circular(8.0)),
              ),
              textStyle: TextStyle(fontSize: 20, color: Colors.white),
              message: imagesAndInfo[index][1],
              child: Image.network(
                imagesAndInfo[index][0],
              ),
            ),
          );
        },
      ),
    );
  }
}

Displaying Assorted Widgets Inside TableView in Flutter

const natureUrls = [
  'https://bit.ly/3dAtFwy',
  'https://bit.ly/36cHehe',
  'https://bit.ly/365uqt1',
  'https://bit.ly/3x7J5Qt',
  'https://bit.ly/3jBvJYU',
  'https://bit.ly/3yhbHHi'
];

extension ToImage on String {
  Widget toPaddedNetworkImage() => Padding(
        padding: const EdgeInsets.all(8.0),
        child: Image.network(this),
      );
}

extension ToImages on List<String> {
  List<Widget> toPaddedNetworkImages() =>
      map((str) => str.toPaddedNetworkImage()).toList();
}

extension ToTableRow on List<Widget> {
  TableRow toTableRow() => TableRow(children: this);
}

class ListPaginator<T> extends Iterable {
  final List<List<T>> list;
  ListPaginator({required List<T> input, required int itemsPerPage})
      : list = [
          for (var i = 0; i < input.length; i += itemsPerPage)
            input.getRange(i, min(input.length, i + itemsPerPage)).toList(),
        ];

  @override
  Iterator get iterator => list.iterator;
}

class HomePage extends StatelessWidget {
  final provider = ListPaginator<String>(
    input: natureUrls,
    itemsPerPage: 3,
  );
  HomePage({Key? key}) : super(key: key);

  Iterable<TableRow> getRows() sync* {
    for (final List<String> urlBatch in provider) {
      final networkImages = urlBatch.toPaddedNetworkImages();
      yield TableRow(children: networkImages);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('TableView in Flutter'),
      ),
      body: SingleChildScrollView(
        child: Table(
          defaultVerticalAlignment: TableCellVerticalAlignment.bottom,
          children: getRows().toList(),
        ),
      ),
    );
  }
}

Page Indicator with Page View in Flutter

const dashes = [
  'https://bit.ly/3gHlTCU',
  'https://bit.ly/3wOLO1c',
  'https://bit.ly/3cXWD9j',
  'https://bit.ly/3gT5Qk2',
];

class PageText extends StatelessWidget {
  final int current;
  final int total;

  const PageText({
    Key? key,
    required this.current,
    required this.total,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(
      'Page ${current + 1} of $total',
      style: TextStyle(fontSize: 30.0, shadows: [
        Shadow(
          offset: Offset(0.0, 1.0),
          blurRadius: 20.0,
          color: Colors.black.withAlpha(140),
        )
      ]),
    );
  }
}

class _HomePageState extends State<HomePage> {
  var _index = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Page Indicator')),
      body: SafeArea(
        child: Column(
          children: [
            Expanded(
              child: PageView.builder(
                onPageChanged: (pageIndex) {
                  setState(() => _index = pageIndex);
                },
                scrollDirection: Axis.horizontal,
                itemCount: dashes.length,
                itemBuilder: (context, index) {
                  return Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Image.network(dashes[index]),
                      Text('Dash #${index + 1}'),
                    ],
                  );
                },
              ),
            ),
            PageText(current: _index, total: dashes.length)
          ],
        ),
      ),
    );
  }
}

Animating and Moving a Floating Action Button in Flutter

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

const List<FloatingActionButtonLocation> locations = [
  FloatingActionButtonLocation.centerDocked,
  FloatingActionButtonLocation.startDocked,
  FloatingActionButtonLocation.startFloat,
  FloatingActionButtonLocation.centerFloat,
  FloatingActionButtonLocation.endFloat,
  FloatingActionButtonLocation.endDocked
];

extension GoAround<T> on List<T> {
  T elementByGoingAround(int index) {
    final finalIndex = index >= length ? index.remainder(length) : index;
    return this[finalIndex];
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  var _locationIndex = 0;

  FloatingActionButtonLocation get location =>
      locations.elementByGoingAround(_locationIndex);

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: Text('Floating Action Button'),
      ),
      floatingActionButtonLocation: location,
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() => _locationIndex += 1);
        },
        child: Icon(Icons.add),
      ),
      bottomNavigationBar: BottomNavigationBar(
        backgroundColor: Colors.yellow[600],
        selectedItemColor: Colors.black,
        items: [
          BottomNavigationBarItem(
            icon: Icon(Icons.bedtime),
            label: 'Item 1',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.access_alarms),
            label: 'Item 2',
          )
        ],
        currentIndex: 0,
      ),
    );
  }
}

Fading Network Image Widget in Flutter

class FadingNetworkImage extends StatefulWidget {
  final String url;

  const FadingNetworkImage({Key? key, required this.url}) : super(key: key);

  @override
  _FadingNetworkImageState createState() => _FadingNetworkImageState();
}

class _FadingNetworkImageState extends State<FadingNetworkImage>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller;
  late final Animation<double> _animation;

  @override
  void initState() {
    super.initState();

    _controller =
        AnimationController(vsync: this, duration: Duration(seconds: 1));

    _animation = Tween(begin: 0.0, end: 1.0).animate(_controller);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Image.network(
      widget.url,
      frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
        _controller.reset();
        _controller.forward();
        return FadeTransition(opacity: _animation, child: child);
      },
      loadingBuilder: (context, child, loadingProgress) {
        final totalBytes = loadingProgress?.expectedTotalBytes;
        final bytesLoaded = loadingProgress?.cumulativeBytesLoaded;
        if (totalBytes != null && bytesLoaded != null) {
          return LinearProgressIndicator(
            value: bytesLoaded / totalBytes,
          );
        } else {
          return child;
        }
      },
      errorBuilder: (context, error, stackTrace) {
        return Text('Error!');
      },
    );
  }
}

const dashes = [
  'https://bit.ly/3gHlTCU',
  'https://bit.ly/3wOLO1c',
  'https://bit.ly/3cXWD9j',
  'https://bit.ly/3gT5Qk2',
];

extension GoAround<T> on List<T> {
  T elementByGoingAround(int index) {
    final finalIndex = index >= length ? index.remainder(length) : index;
    return this[finalIndex];
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _index = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Faded Image'),
      ),
      body: Center(
          child: Column(
        children: [
          FadingNetworkImage(
            url: dashes.elementByGoingAround(_index),
          ),
          TextButton(
            onPressed: () {
              setState(() => _index += 1);
            },
            child: Text('Load next Dash'),
          ),
        ],
      )),
    );
  }
}

Transparent Alert Dialogs in Flutter

TextStyle get whiteTextStyle => TextStyle(color: Colors.white);

Future<void> showTextDialog({
  required BuildContext context,
  required String text,
}) {
  return showDialog(
    context: context,
    builder: (context) {
      return AlertDialog(
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.all(
            Radius.circular(10),
          ),
          side: BorderSide(
            color: Colors.white,
            style: BorderStyle.solid,
            width: 2,
          ),
        ),
        backgroundColor: Colors.black.withAlpha(150),
        titleTextStyle: whiteTextStyle,
        contentTextStyle: whiteTextStyle,
        content: Text(text),
        actions: [
          TextButton(
            style: TextButton.styleFrom(primary: Colors.white),
            onPressed: () {
              Navigator.of(context).pop();
            },
            child: Text('OK'),
          )
        ],
      );
    },
  );
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(
          'Rounded Corder Dialog',
        ),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Image.network('https://bit.ly/3ywI8l6'),
          TextButton(
            onPressed: () async {
              await showTextDialog(
                context: context,
                text: 'Hello world',
              );
            },
            child: Text('Show dialog'),
          ),
        ],
      ),
    );
  }
}

Network Image Size in Dart

import 'dart:ui' as ui;

Future<Size> getImageSize(String uri) {
  final image = Image.network('https://bit.ly/3dAtFwy');
  final comp = Completer<ui.Image>();
  image.image
      .resolve(
        ImageConfiguration.empty,
      )
      .addListener(
        ImageStreamListener(
          (ImageInfo info, _) => comp.complete(info.image),
        ),
      );
  return comp.future.then(
    (image) => Size(
      image.width.toDouble(),
      image.height.toDouble(),
    ),
  );
}

void testIt() async {
  final imageSize = await getImageSize('https://bit.ly/3dAtFwy');
  print(imageSize);
  assert(imageSize.width == 2048.0);
  assert(imageSize.height == 1365.0);
  print(imageSize.aspectRatio);
}

Animated Icons in Flutter

class _HomePageState extends State<HomePage>
    with SingleTickerProviderStateMixin {
  late final Animation<double> _animation;
  late final AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 1),
    );
    _animation = Tween(
      begin: 0.0,
      end: 1.0,
    ).animate(_controller);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    _controller.repeat(reverse: true);
    return Scaffold(
      appBar: AppBar(
        title: Text('Animated Icons in Fluter'),
      ),
      body: Center(
        child: AnimatedIcon(
          color: Colors.green[300],
          size: MediaQuery.of(context).size.width,
          icon: AnimatedIcons.search_ellipsis,
          progress: _animation,
        ),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

Custom Scroll Views in Flutter

const gridImages = [
  'https://bit.ly/3x7J5Qt',
  'https://bit.ly/3dLJNeD',
  'https://bit.ly/3ywI8l6',
  'https://bit.ly/3jRSRCu',
  'https://bit.ly/36fNNj9',
  'https://bit.ly/3jOueGG',
  'https://bit.ly/3qYOtDm',
  'https://bit.ly/3wt11Ec',
  'https://bit.ly/3yvFg7X',
  'https://bit.ly/3ywzOla',
  'https://bit.ly/3wnASpW',
  'https://bit.ly/3jXSDto',
];

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: [
          CustomAppBar(),
          CustomGridView(),
          CustomListView(),
        ],
      ),
    );
  }
}

class CustomListView extends StatelessWidget {
  const CustomListView({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SliverPadding(
      padding: EdgeInsets.all(8.0),
      sliver: SliverList(
        delegate: SliverChildBuilderDelegate(
          (context, index) {
            return Padding(
              padding: const EdgeInsets.only(bottom: 8.0),
              child: Image.network(gridImages[index]),
            );
          },
          childCount: gridImages.length,
        ),
      ),
    );
  }
}

class CustomGridView extends StatelessWidget {
  const CustomGridView({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SliverPadding(
      padding: EdgeInsets.all(8.0),
      sliver: SliverGrid(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 3,
          mainAxisSpacing: 10,
          crossAxisSpacing: 10,
          childAspectRatio: 1.0,
        ),
        delegate: SliverChildBuilderDelegate(
          (context, index) {
            return Container(
              width: 100,
              height: 100,
              child: Image.network(gridImages[index]),
            );
          },
          childCount: gridImages.length,
        ),
      ),
    );
  }
}

class CustomAppBar extends StatelessWidget {
  const CustomAppBar({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SliverAppBar(
      backgroundColor: Colors.orange[300],
      forceElevated: true,
      pinned: false,
      snap: false,
      floating: true,
      expandedHeight: 172,
      flexibleSpace: FlexibleSpaceBar(
        title: Text(
          'Flutter',
          style: TextStyle(
            fontSize: 30,
            color: Colors.white,
            decoration: TextDecoration.underline,
          ),
        ),
        collapseMode: CollapseMode.parallax,
        background: Image.network('https://bit.ly/3x7J5Qt'),
      ),
    );
  }
}

Parallax App Bar in Flutter

JSON HTTP Requests in Dart

URL Timeouts in Dart

Detecting URL File Types in Dart

Paginated Lists in Dart

Requesting DELETE on APIs in Dart

Animated Containers in Flutter

Hiding Widgets in Flutter

Simple Opacity Animation in Flutter

Vignette Widget in Flutter

Drop Down Button Configuration and Usage in Flutter

Expandable List Items in Flutter

Infinite Scrolling in Flutter

Infinite Arrays in Dart

Custom Color Picker Component in Flutter

Displaying and Reacting to Switches in Flutter

Displaying Bottom Bars in Flutter

Displaying Buttons on AppBar in Flutter

Displaying Bottom Sheets in Flutter

Converting Enums to Radio Buttons in Flutter

Check Existence of Websites in Flutter

Images inside AlertDialog in Flutter

Returning Values from AlertDialog in Flutter

Simple Grid View in Flutter

Rendering Bullet Points in Flutter

Retrying Futures in Flutter

Containers as ClipOvals in Flutter

Rich Texts in Flutter

Wrapping Widgets in Flutter

Sweep Gradients in Flutter

Stream and StreamBuilder in Flutter

Blur Effect in Flutter

Convert Enums to Strings in Dart

Replacing Text in TextField in Flutter

Aspect Ratio in Flutter

Zoom and Pan in Flutter

Resizing Images in Flutter to Fit Screen Height

Validating URLs in Flutter

FrameBuilder for Network Images in Flutter

Adding Shadow to Icons in Flutter

Calculating Median of Lists in Dart

Generic Functions with Reduce in Dart

Passing Back Data From a Screen to the Previous One in Flutter

Flinging an Animation in Flutter

Fade Animations in Flutter

Throttling User Input in Flutter

Censoring TextFields in Flutter

Customizing TextButton in Flutter

Multiline TextFields in Flutter

Filtering TextField Input in Flutter

Focusing Manually on TextFields in Flutter

Data Streams Over HTTP/HTTPs in Dart

Catching Nonexistent Accessors or Methods in Dart

Using Expando in Dart

Implementing Custom Maps in Dart

Dynamically Calling Functions in Dart

Factory Constructors in Dart

Calculating the Sum of List Items in Dart

Removing Duplicate Strings in Lists in Dart (Case-Insensitive)

Implementing Range in Dart

Converting Lists to Maps in Dart

Implementing Hashable in Dart

Random Name Generator in Dart

Capturing Stack Traces in Dart Exceptions

Removing Duplicates from Lists in Dart

Optional Spread Operator in Dart

Calling Optional Functions in Dart

Odd-Even Sort in Dart

Implementing Zip and Tuples in Dart

Swapping Values in Lists with XOR in Dart

Waiting for Multiple Futures in Dart

Using Queues as Stacks in Dart

Custom Iterators in Dart

Iterables as Ranges and Transform in Dart

Errors vs Exceptions in Dart

Custom Annotations in Dart

Classes as Enums in Dart

Spread Operator in Collection Literals in Dart

StreamBuilder and StreamController in Dart

Almost Equal in Dart

Enum Associated Values in Dart

Implementing Comparable in Dart

Implementing Custom Integer Types in Dart

Custom Subscripts in Dart

Dart List Enumeration with Index

Applying Mixins to Other Mixins in Dart

Parameter Types in Dart

Custom Exceptions in Dart

rethrowing Exceptions in Dart

mixins and JSON Parsing in Dart

mixins vs abstract classes in Dart

Drawing Shapes in Flutter with LayoutBuilder, CustomPaint and CustomPainter

Generic Type Aliases in Dart

Callable Classes in Dart

Synchronous Generators in Dart

Implicit Interfaces in Dart

Did you know that in #Dart, every #class implicitly exports an #interface that can be #implemented (as opposed to #extended) by other classes? This is called "implicit interface".

Do you know how "const" constructors work in #Dart?

Did you know that in #Dart, it is actually preferred to use #async and #await over using raw #Futures?

In #Dart, you can use a combination of #Initializer #List plus default values for your class #member #fields to create elegant and handy convenience initializers

Did you know that in #Dart, you can extract elements of a certain type from your Lists using the #whereType #generic #function instead of calculating the #equality yourselves?

Do you know about #Type #Promotion in Dart?

"address" is an optional field of the "Person" class. If you look at the "doThis()" function you see that I'm saving the value of address in a local variable and then comparing it with null and then returning if it's null. The Dart compiler is intelligent enough to understand that after the if-statement, "address" is NOT null anymore since you've already compared it with null and returned from the function.

If you look at the "insteadOfThis" function, the first one, the Dart compiler cannot make the same assumption if you don't first store the value of address in a local variable. In that first function the Dart compiler, even after the if-statement, needs you to refer to address as an optional, using "address?" syntax.

The mechanism the Dart compiler uses in the "doThis()" function is called Type Promotion.

4 lines of #Dart code that include the #spread operator, #cascade #operator, #generics, #extensions, #private prefix and #getters

Functions as First Class Citizens in Dart

Download Details: 
Author: vandadnp
Source Code: https://github.com/vandadnp/flutter-tips-and-tricks

#flutter #dart #programming #developer 

A Collection of Flutter and Dart Tips and Tricks
Marisol  Kuhic

Marisol Kuhic

1627801200

Elvis Operator Example in Kotlin

Kotlin : Elvis Operator Usage Example | Added Subtitles | android coding

For more interesting tutorials on flutter browse though the below tutorials and subscribe us for more updates @ - https://bit.ly/2SmKtdK

Kotlin Complete Course:
Kotlin Introduction : https://youtu.be/2_EXYeKVX3g
Kotlin Variables & Data Types : https://youtu.be/bevfj0pFH6Q

#kotlin #elvis #operator

Elvis Operator Example in Kotlin
Marisol  Kuhic

Marisol Kuhic

1627671600

Kotlin : Arithmetic Operators Usage and Example

In this part of the tutorial we will deal with arithmetic operators in kotlin.

Kotlin Complete Course:
Kotlin Introduction : https://youtu.be/2_EXYeKVX3g
Kotlin Variables & Data Types : https://youtu.be/bevfj0pFH6Q
Kotlin If-Conditions : https://youtu.be/8r0TMiKLwGo
Kotlin When Condition (Switch) : https://youtu.be/oQ3rXxU2bK4
Kotlin For Loop : https://youtu.be/10uMCFiUDNU
Kotlin While & Do-While Loop : https://youtu.be/nXStQ8WFCT0

#subtitle #android #arithmetic #kotlin #operator

Kotlin : Arithmetic Operators Usage and Example