Sometimes we want to get the location of Various common directories like:
The package path_provider
is build for exact same reason so that we do not have to deal with Platform specific issues by our own.
- Add path_provider to pubspect.
path_provider: ^verison
- Use it
//Gives download directory
Directory tempDir = await getTemporaryDirectory();
//Gives documents directory
Directory docDir = await getApplicationDocumentsDirectory();
//Gives external Storages
List<Directory> externalStorages = await getExternalStorageDirectories();
//Gives download directory
Directory downloadDir = await getDownloadsDirectory();
We can use ShapeBorder to give outline to widgets or Clip them on it's shape. There are my ShapeBorder like Border, ContinuousRectangleBorder, StadiumBorder, CircleBorder, BeveledRectangleBorder
etc.
- Use ShapeBorder to give a Widget outline.
Container(
decoration: ShapeDecoration(
color: Colors.white,
shape: // Any shape border
),
)
- Use ShapeBorder to clip a Widget.
ClipPath(
clipper: ShapeBorderClipper(
shape: // Any shape border
),
child: // Any Child to be clipped
)
Do you miss number ranges like these in dart?
for (i in 1..4) print(i) //Kotlin Range
for (i until 1..4) print(i) //Kotlin Range
No problem. Just define this Range Extension on numbers and you will be good to go.
extension Range on num {
List<num> until(num endPoint) {
var exclusive = to(endPoint);
exclusive.removeLast();
return exclusive;
}
List<num> to(num endPoint) {
var numbers = <num>[];
if (endPoint > this) {
for (int i = this; i <= endPoint; i++) {
numbers.add(i);
}
} else {
for (int i = this; i >= endPoint; i--) {
numbers.add(i);
}
}
return numbers;
}
}
Then Simply use them like this:
void main() {
// 2,3,4,5,6,7,8,9,10
for (int i in 2.to(10)) { print(i); }
// 2,3,4,5,6,7,8,9
for (int i in 2.until(10)) { print(i); }
// 2,1,0,-1,-2,-3,-4,-5,-6,-7
for (int i in 2.to(-7)) { print(i); }
// 2,1,0,-1,-2,-3,-4,-5,-6
for (int i in 2.until(-7)) { print(i); }
}
If you want more advanced range and other cool extensions use dartx
If you logically decide wheather to add a particular item into collection or not? It looks like no big deal when we dealing with adding normal objects to a collection as we could simply add collection items inside if or for statement.
But if we want to conditionally add widget or list of them inside another as one as it's children, it's a pain.
From dart 2.3 onwards, we can use collection if
and collection for
operators for adding items to a collection conditionally
or in bulk
.
ListView(
children: [
Title(news.headline),
(news.cover != null) ? FeatureImage(news.cover) : Container(),
...news.paragraphs.map((paragraph) => Paragraph(paragraph)).toList(),
(news.author != null) ? Authored(news.author) : Container(),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
(selected > 0) ? nextButton() : Container(),
(selected < (allNews.length - 1)) ? prevButton() : Container(),
],
)
],
)
ListView(
children: [
Title(news.headline),
if (news.cover != null) FeatureImage(news.cover),
for (var paragraph in news.paragraphs) Paragraph(paragraph),
if (news.author != null) Authored(news.author),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (selected > 0) nextButton(),
if (selected < (allNews.length - 1)) prevButton(),
],
)
],
)
A function call if caller and calling functions have same arguments.
// Use tear-off instead of Function if arguments of caller and calling method match
// ❌ Normal Method call ❌
void logSomething(String abc) => print(abc);
// ✅ Using tear-off ✅
void logSomething(String abc) => print;
// ❌ Normal Method call ❌
pritSum(int first, int second) {
var sum = getSum(first, second);
print(sum);
}
// ✅ Using tear-off ✅
printSum1(int first, int second) => print(getSum);
// ❌ We cannot call tear-off on tear-off ❌
printSum2(int first, int second) => getSum.print;
int getSum(int first, int second) => first + second;
A lambda.
// Use 'tear-off' closure instead of lambda when possible
// ❌ Using Lambda ❌
var button = MaterialButton( onPressed: () { buttonPressed(); } );
// ✅ Using tear-off ✅
var button1 = MaterialButton( onPressed: buttonPressed );
void buttonPressed() => print("Button Pressed");
// ❌ Using Lambda ❌
printFirstNSquares1(int count) {
1.to(count)
.map((number) {return getSquare(number); })
.foreach((square) { print(square); } );
}
// ✅ Using tear-off ✅
printFirstNSquares(int count) => 1.to(count).map(getSquare).forEach(print);
int getSquare(int number) => number * number;
Want to apply filter to image or any widget? Use ColorFiltered widget like this.
ColorFiltered(
colorFilter :ColorFilter.mode(Colors.pink,BlendMode.multiply),
child: //Widget
)
Try different BlendMode and colors.
ColorFiltered works with any widgets as child not just Image.
If you want to apply gradient mask or Image mask to any widget in flutter ShaderMask is the tool to use.
Just give blendMode
and shaderCallback
to ShaderMask along with the Child that you want to mask.
Gradients can be easily converted to Shader with createShader() method.
ShaderMask(
blendMode: BlendMode.srcIn,
shaderCallback: (Rect bound) {
return LinearGradient(colors: <Color>[
Colors.deepOrange,
Colors.blue,
Colors.green,
Colors.amber,
]).createShader(bound);
},
child: Icon(
Icons.ac_unit,
size: 200,
),
)
In languages like Java there is a synchronized
keyboard that acts as lock for preventing concurrent access like while handling transactions.
In dart we have a package called synchronized
. Add synchronized: ^latest_version
to pubspec.yaml
then start using it by:
Simply wrapping the transaction / block to be synchronized inside synchronized()
and that block won't be called again until the previous call is finished.
import 'package:synchronized/extension.dart';
main() async {
var demo = Demo();
await demo.runSynchronized(); // prints 12341234
await demo.runNotSynchronized();// prints 11223344
}
class Demo {
Future runNotSynchronized() async {
stdout.writeln('not synchronized');
write1234();
write1234();
await Future.delayed(const Duration(milliseconds: 300));
stdout.writeln();
}
Future runSynchronized() async {
stdout.writeln('synchronized');
synchronized(() async { await write1234(); });
synchronized(write1234);
await Future.delayed(const Duration(milliseconds: 300));
stdout.writeln();
}
Future write1234() async {
for (var value in [1, 2, 3, 4]) {
await Future.delayed(const Duration(milliseconds: 30));
stdout.write(value);
}
}
}
In almost every app we need circular image (with a border & shadow).
Just wrap the Image like this :
Widget/Image()
-Inside-> ClipRRect()
-Inside-> Container()
(with circular BoxDecoration and boxShadow)
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(200),
border: Border.all(color: Colors.indigoAccent, width: 8),
boxShadow: [
BoxShadow(
color: Color(0x332222CC),
blurRadius: 6,
spreadRadius: 6,
offset: Offset.fromDirection(0, 0)),
]
),
child: ClipRRect(
borderRadius: BorderRadius.circular(400),
child: Image.network("imageUrl",height: 200,width: 200,),
),
),
Flutter has special keyword to check if an instance is not a type
.
var name = "Hello World";
// ✅ Right way ✅
assert(name is String);
//❌ Wrong way ❌
assert(!(name is int));
// ✅ Right way ✅
assert( name is! int);
Have you been using static methods to initialze Objects with name?
Use named constructor :
class Color {
Color({this.r, this.b, this.g});
int r = 0, g = 0, b = 0;
// ❌ static method
static Color red() => Color(r: 255,g: 0,b:255);
// ✅ named constructor
Color.red() {
r = 255;
}
// ❌ static method
static Color cyan() => Color(r:0, b: 128, g: 128);
// ✅ named constructor
Color.cyan(): g = 128, b = 128;
// ✅ named constructor
Color.cyan() {
g = 128;
b = 128;
}
}
as :
If we import two or more libraries that have conflicting identifiers, we can use them with prefix.
given after as
keyword.
import 'package:library.dart' as lib;
show :
Show only certain class and hide everything else from it.
import 'dart:math' show max,tan;
hide :
Hide only certain class & show everything else from it.
import 'dart:core' hide String;
final and const
in dart are confusing to the level we think both of them are same. Let's see their diffrences
final (global / static / member variables) |
const (static / global variables) |
---|---|
Has single value of a member or static or global variable from start to end. |
Has single value of a static or global variable from start to end. |
Different objects of same type can have different final values in same member varaible. | Different objects of same type cannot have different const values in same varaible. (i.e. const members should be static ) |
Should be initialzed before constructor is called . |
Should be strictly initialized during declaration . |
Only the final declared member/staic/global variable objects are immutable, their content variables may not. i.e. variables inside final objects can be udpated / reassigned if they are not final themselves. |
All const declared Golbal/static variable objects including all their internal content varibales are immutable and cannot be changed. |
Can be intialized with immutable/ mutable / calculated values determined at compile time or runtime . |
Can be initialized with only immutable values with all their immutable internal varaibles, determined at compile time . |
We can take example of various computers and how they can be compared with dart modifiers.
If you want to display data from API backend or any async source, use FutureBuilder.
FutureBuilder will automatically give you Widget with default value until the data arrives from API/ async Source.
As soon as the real data arrives, it will rebuild the Widget with actual data.
Just provide the async function / source that will return the values asynchronously along with initial(default) data.
It works very similar to StreamBuilder
//Future Builder
FutureBuilder<List<Player>>(
initialData: [],
future: FakePlayersAPI().getPlayers(),
builder: (context, snapshot) {
if (snapshot.data.isEmpty) return CircularProgressIndicator()
else return PlayersListWidget(snapshot.data)
}
)
//Fake Backed API.
class FakePlayersAPI {
Future<List<Player>> getPlayers() async {
await Future.delayed(Duration(seconds: 1));
return _players;
}
Future<String> getPlayerStory() async {
await Future.delayed(Duration(seconds: 1));
return playerStoryText;
}
}
Bored of writing PageRoute
,context
and builder
without actual use of them just to navigate to different Widget? Use get: ^version
library.
Also this gives us superpower to navigate/show dialog/ BottomSheet/ Snackbar
withoutBuildContext
, there might be cool patterns to be developed with this freedom.
- Just Replace your MaterialApp with
GetMaterialApp
. - Start navigating like this :
//Go to next screen
Get.to(NextScreen());
//Show dialog
Get.dialog(MyDialogWidget());
//Show bottom sheet
Get.bottomSheet(MyButtomSheetWidget)
//Show snackbar
Get.snackbar(title, subTitle);
Maintaining latest versions of pub dependencies is pain. Even worse, every latest versions are compatible.
From dart 2.8 onwards, the command pub outdated
gives you the overview of your dependencies and possible upgrades in Resolvable column
.
🚨🚨Tip: use flutter pub outdated
if pub outdated
can't find Flutter SDK.
typedef
is used when you want to give an alias to a Function signature
. As it can be laborous and error prone to type the Function Signature in every field or return type. As a bonus, we do not lose Function's type info.
Steps to use typedef:
Step 1 : Define a typedef
with Function Signature outside a class
.
typedef UserSelector = User Function(String, String);
Step 2 : Replace the Function Signature with typedef to declare members or return type.
class A{
//Use this ✅
UserSelector userSelector;
// Not this ❌
User Function(String, String) userSelector;
A ({this.userSelector});
}
Step: Simply pass function/ lambda like you did before.
//Pass lambda
var a = A(userSelector: (id, authKey) {
return User(id, "User name");
});
//Pass Method
var a = A(userSelector: getUser)
User getUser(String id, String key) {
return User(id, "User name");
}
Still confused installing flutter in mac/linux easily?
- Download Flutter zip.
- Decompress it in desired directory.
cd
to that desired directory from terminal. [🚨⚠️ flutter should be inside desired directory]- Run the following command
# If you are using zsh:
echo "\nexport PATH="\$PATH:`pwd`/flutter/bin"">>~/.zshrc ; source ~/.zshrc
# If you are using bash:
echo "\nexport PATH="\$PATH:`pwd`/flutter/bin"">>~/.bashrc ; source ~/.bashrc
Black lives matter ✊🏿& Racial Equality should be the norm.
This is a tip not just for Flutter developers but to all of us human.
This is my tip number #59.
Even though flutter doesn't support any vector graphics out of the box, the package flutter_svg
can render svg
into Flutter app.
-
Add dependency :
flutter_svg: ^version
-
Start using it like Flutter
Image
Widget.
//load SVG from network
SvgPicture.network("https://image.flaticon.com/icons/svg/124/124555.svg", height: 200)
//load SVG from assets folder
SvgPicture.asset("assets/ufo.svg", height: 200)