Similar to vector graphics, Flutter doesn't support vector animation natively. Rive (rive.app) helps with amazing vector animations in Flutter.
- Add flare in
pubspec.yaml
:
flare_flutter: ^version
- Download flare files from rive.app & put those
.flr
files into
`assets/` folder:
assets:
- assets/
- Start using
FlareActor
widget.
FlareActor(
"assets/world.flr",
//🚨Caution🚨, you can find 👉 #animation name in
//left bottom 👈👇 of rive.app designer tool when
//Animation tab is selected
animation: "world")
When we want to add a dependency only way is either going to github document or pub.dev.
We can directly add dependencies from our IDEs using extensions/plugins.
For VS code : Pubspec Assist |
For Android Studio : Flutter Enhancement Suite |
---|---|
P.S. Flutter Enhancement Suite comes with a lot of other great features.
cached_network_image
helps to show
and cache
image from Internet.
It shows image from cache if it's already cached instead of downloading.
It will make app feel faster and save network bandwidth.
- Add cached_network_image in pubspec.yaml
dependencies:
cached_network_image: ^version
- Start using
CachedNetworkImage
.
CachedNetworkImage(
imageUrl: imageUrl,
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
)
Image.network | CachedNetworkImage |
---|---|
get_it
helps you to access :
- Service objects like
RESTClient
,DB
& theirmocks
from anywhere. (View/UI)Model,BLoCs
etc. fromWidgets
.
without BuildContext
.
-
Add get_it to dependencies
dependencies: get_it: ^4.0.2
-
Provide an object/dependency to get_it
void main() { GetIt.I.registerSingleton(Player.messi()); runApp(MyApp()); }
-
Get the dependency from anywhere in app with it's type.
Player currentPlayer = GetIt.I<Player>(); print(currentPlayer) // Player{name: Messi,shirtNumber: 10}
Lint helps you to find potential errors, bugs, & code style inconsistancy etc.
To setup lint in Flutter :
- Add lint in dev dependencies.
dev_dependencies:
lint: ^version
-
Include
package:lint/analysis_options.yaml
insideanalysis_options.yaml
and add your custom rules.
include: package:lint/analysis_options.yaml
# Custom Rules
linter:
rules:
sort_constructors_first: true
prefer_single_quotes: true
- Done.
If some conditions
must not ever occur
on program, we use assert to halt the execution.
Assert condition means that there is no way we can handle exception caused if the condition fails, it must be fixed.
assert()
statement also help reduce the responsibility of a program as it doesn't need to handle every edge cases.
assert()
are ignored in release/production.
Some examples :
updateUser(User user){
assert(user.id != null) // There is no way to udpate user without id.
syncUser(user);
}
int getRealSquareRoot(int square){
assert(square >= 0) //square must be positive to have real root.
return sqrt(square);
}
driveCar(Car car){
assert(car.hasEngine);
assert(car.hasFuel);
assert(car.hasWheels);
car.drive();
}
- Create
.github/workflows/main.yml
Inside your project's root directory. or Run the command in your terminal / Powershell :
md .github/workflows && cd "$_" && touch main.yml
-
Put the steps in this file inside
main.yml
-
Add the build badge on your README.md.
[![Dart CI]({YOUR_GITHUB_PROJECT_URL}/workflows/Flutter%20CI/badge.svg)]({YOUR_GITHUB_PROJECT_URL}/actions)
-
Commit to Github.
-
Add the following steps at the end of your Github Actions main.yml from previous tips. Find the full
main.yml
file here- uses: codecov/[email protected] if: matrix.os == 'ubuntu-latest' with: token: ${{secrets.CODECOV}} #required file: ./coverage/lcov.info #optional
-
Login/Sign up to codecov.io
-
Go to https://codecov.io/gh > click on your username > search the repo to show codecoverage and select it.
-
After that copy the Uplaod token (which should be staring at you at this point/inside setting tab)
-
Go to project's setting(
not profile setting
), select "Secrets" from left navigation panel, Add new secret -
Add code Coverage badge to your github repo README file.
[![codecov](https://codecov.io/gh/USER_NAME/REPO_NAME/branch/master/graph/badge.svg)](https://codecov.io/gh/USER_NAME/REPO_NAME)
-
Git Push and wait for the build to complete, you will have the badges in your github Repo like this:
Instead of using static
methods to create/return
new/cached
instance of it's class or it's sub classi.e. factory pattern
, we can use factory constructors
.
factory
constructors behave like static methods
but called like normal constructors
. Factory constructors can also be be named & unnamed.
void main() {
//❌ static method ❌
var staticUser = User.getUser("John Doe");
//✅ factory connstructor ✅
var factoryUser = User("John Doe");
}
class User {
User._(this.name);
String name;
static Map<String, User> users = {};
//❌ static method ❌
static User getUser(String name) => users.putIfAbsent(name, () => User._(name));
//✅ factory connstructor ✅
factory User(String name) => users.putIfAbsent(name, () => User._(name));
}
We can animate change in TextStyle with AnimatedDefaultTextStyle
.
Just give the animation duration
& the updated TextStyle
. AnimatedDefaultTextStyle
will take care of the rest.
AnimatedDefaultTextStyle(
duration: Duration(milliseconds: 300),
child: Text("Flutter"),
style: newStyle,
)
MediaQuery
gives the information about screen like height
, width
, pixels
, notch size
, Device scale factor & text scale factor set on setting, device theme light/dark
, system animation enabled/disabled
etc.
try on codepen
Want to get desired number of digits after decimal?
- Use
number.toStringAsFixed()
- Parse the String to number.
For convenience we can use extension functions. try on dartpad
You can multiply String like numbers.
"Foo"*2 //Foo
"Bar "*5 //Bar Bar Bar Bar Bar
Getting value of enum
is not trivial in dart. This simple extension function can get rid of Pain in the enum.
- Define this extension
- Start calling
.asEnum()
in anyenum
to print theValue
. - You can use underscore
_
if you want space between words.
fastfood 🍔/ plugin 🧩/ an interface with already implemented methods & state, that is ready to be used without reimplementing those features everywhere we need them
When paired up with a StatefulWidget
's State
,TickerProviderStateMixin
creates ticker
that ticks with every frame which is need by every AnimationController . It also disposes ticker when stateful widget disposes. That's why we provide this
as TickerProvider(vsync
) in every AnimationController.
Similarly we use ListMixin to use obvious implementation of List so that we do not have to implement obvious stuffs in every List implementation like ElementList,NodeList,FileList,TouchList
etc.
extends (inheritance)
=> only one class can be inherited along with their public/protected members and behaviors.
implements (contract)
=> many classes can be implemented but we have to redefine every behavior.
with(mixin)
=> Many classes can be mixed in and we can reuse the behavior of them.
Any class or abstract class can be used as mixin. But we declare mixin, it cannot be extended like normal class or abstract class.
class A{} //Declaring class
mixin B{} //Declaring mixin
class C extends A{} // Valid ✅
class C extends B{} // Invalid ❌
A mixin cannot use another mixin.
mixin C with B{} // Invalid ❌
CustomPainter provides canvas to draw different shapes.
- Define a class and extend CustomPainter.
- Override paint(canvas,size) method and draw various shapes(circle,arc,rectangle,line etc) inside it.
- Add a CustomPaint widget on screen and pass the CustomPainter as paint and it's size.
Output
Do you want pause program flow for a certain duration? You can use Future.delayed()
:
await Future.delayed( duration )
Have you been navigating to different screen like this?
❌ Dont do this anymore ❌
Navigator.of(this).push(
MaterialPageRoute(builder: (context) => NextScreen()),
);
✅ Do this ✅
-
Define a simple extension on
BuildContext
.extension NavigationExtension on BuildContext { navigateTo(Widget destination) { Navigator.of(this).push( MaterialPageRoute(builder: (context) => destination), ); } }
-
🎉 Celebrate 🎉 🎉
context.navigateTo( NextScreen()); context.navigateTo( SeventhScreen()); context.navigateTo( SettingPage());
It's too much of work 😰🥱 to import every assets like image 🏞, json , audio 🎵into pubspec.yml
But sadly 😢, you can't import just the assets
folder.
Just import all the folders, it will work 🎉🍾.
Instead of using raw string, path & URLs, we can organize them in Resource classes.
Group constants, paths and strings by their types
class Strings {
String appName = "Productive";
String privacyURL = "https://blabla.com";
}
class SVGImages { String homeIcon = "assets/images/home.svg";}
class LottieFiles { String paperPlane = "assets/lottie/paper_plane.json";}
Organize all resource into one class
class R {
static Strings strings = Strings();
static SVGImages svgImages = SVGImages();
static LottieFiles lottieFiles = LottieFiles();
}
Use resources instead of raw strings and path
// ❌ Don't use raw Strings & Paths ❌
Text("Productive"),
Image.asset("assets/images/home.svg"),
Lottie.asset("assets/lottie/paper_plane.json"),
// ✅ Acess String & Pathsfrom Resource ✅
Text(R.strings.appName),
Image.asset(R.svgImages.homeIcon),
Lottie.asset(R.lottieFiles.paperPlane),