Flutter - Other

Understanding the pubspec.yaml File

File Structure and Syntax


  • The pubspec.yaml file is written in YAML (YAML Ain't Markup Language), which is a human-readable data serialization standard.
  • YAML uses indentation to represent hierarchy, so it's important to maintain consistent spacing (two spaces are standard) and avoid tabs.


Key Sections of pubspec.yaml

Here's an overview of the primary sections you'll find in a pubspec.yaml file:


Metadata

At the top of the file, you'll find metadata about your project:

name: your_project_name
description: A brief description of your project
publish_to: 'none' # Remove this line if you want to publish to pub.dev
version: 1.0.0+1 # The version number and build number


  • name: The name of your package or project. It should be lowercase and can include underscores.
  • description: A short description of what your package does.
  • publish_to: Specifies where the package will be published. Use 'none' to prevent publishing to pub.dev.
  • version: Defines the version of your package. The format is major.minor.patch+build.


Environment

This section specifies the Dart SDK version your project supports:

environment:
  sdk: '>=2.12.0 <3.0.0'


  • sdk: Use a version constraint to define the minimum and maximum versions of the Dart SDK that your project is compatible with.


Dependencies

This section lists the packages your project depends on:

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  http: ^0.14.0


  • dev_dependencies: Use this to list packages that are only needed for development and testing.


Flutter Specific

If your project is a Flutter project, you'll want to include a flutter section:

flutter:
  uses-material-design: true
  assets:
    - assets/images/
    - assets/icons/


  • uses-material-design: Set this to true to enable Material Design features in your app.
  • assets: List assets (like images or fonts) that should be included in your app. You can specify individual files or entire directories.


Fonts

To include custom fonts in your project, use the following structure:

fonts:
  - family: Roboto
    fonts:
      - asset: fonts/Roboto-Regular.ttf
      - asset: fonts/Roboto-Bold.ttf
        weight: 700


  • fonts: Define custom fonts and their variations. You can specify different weights and styles.


Dependency Overrides

In some cases, you might need to override specific dependencies:

dependency_overrides:
  http: ^0.13.3


  • dependency_overrides: Use this to specify a version of a dependency that should be used instead of the version specified by other packages.


Best Practices


  • Keep it Clean: Remove any unnecessary comments or empty lines to maintain readability.
  • Version Control: Regularly update dependency versions and ensure compatibility.
  • Use Semantic Versioning: Follow semantic versioning to avoid conflicts and issues during dependency resolution.


Commands to Manage Dependencies

You can manage dependencies in your project using the following commands in the terminal:


  • Get Dependencies: This command fetches the packages listed in pubspec.yaml. flutter pub get
  • Upgrade Dependencies: This updates all dependencies to the latest compatible versions. flutter pub upgrade


Example of a Complete pubspec.yaml

Here's an example of a complete pubspec.yaml file for a Flutter project:

name: my_flutter_app
description: A sample Flutter application
publish_to: 'none'
version: 1.0.0+1

environment:
  sdk: '>=2.12.0 <3.0.0'

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  http: ^0.14.0

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:
  uses-material-design: true
  assets:
    - assets/images/
    - assets/icons/
  fonts:
    - family: Roboto
      fonts:
        - asset: fonts/Roboto-Regular.ttf
        - asset: fonts/Roboto-Bold.ttf
          weight: 700

dependency_overrides:
  http: ^0.13.3

Use Cascades Operator

If we want to perform a sequence of operations on the same object then we should use the Cascades(..) operator.


Do

var path = Path()
	..lineTo(0, size.height)
	..lineTo(size.width, size.height)
	..lineTo(size.width, 0)
	..close(); 


Don't

var path = Path();
	path.lineTo(0, size.height);
	path.lineTo(size.width, size.height);
	path.lineTo(size.width, 0);
	path.close(); 

Use Const in Widgets

The widget will not change when setState call we should define it as constant. It will prevent the widget to rebuild so it improves performance.


Container(
  padding: const EdgeInsets.only(top: 10),
  color: Colors.black,
  child: const Center(
    child: const Text(
      "No Data found",
      style: const TextStyle(fontSize: 30, fontWeight: FontWeight.w800),
    ),
  ),
);

Use dart:io library to write a platform-specific code

import 'dart:io' show Platform;

...

if(Platform.isIOS) {
  doSomethingforIOS();
}

if(Platform.isAndroid) {
  doSomethingforAndroid();
}

Use expression function bodies

For functions that contain just one expression, you can use an expression function. The => (arrow) notation is used for expression function.


Do

get width => right - left;
	
Widget getProgressBar() => CircularProgressIndicator(
  valueColor: AlwaysStoppedAnimation<Color>(Colors.blue),
);


Don't

get width {
  return right - left;
}
 
Widget getProgressBar() {
  return CircularProgressIndicator(
    valueColor: AlwaysStoppedAnimation<Color>(Colors.blue),
  );
}

Use if condition instead of conditional expression

Many times we need to render a widget based on some conditions in Row and Column. If conditional expression return null in any case then we should use if condition only.


Do

Widget getText(BuildContext context) {
  return Row(
    children: [
      Text("Hello"), 
      if (Platform.isAndroid) Text("Android")
    ]
  );
}


Don't

Widget getText(BuildContext context) {
  return Row(
    children: [
      Text("Hello"),
      Platform.isAndroid ? Text("Android") : null,
      Platform.isAndroid ? Text("Android") : SizeBox(),
      Platform.isAndroid ? Text("Android") : Container(),
    ]
  );
}

Use interpolation to compose strings

Use interpolation to make string cleaner and shorter rather than long chains of + to build a string.


Do

var description = 'Hello, $name! You are ${year - birth} years old.';


Don't

var description = 'Hello, ' + name + '! You are ' + (year - birth).toString() + ' years old.';

Use Map Instead of Switch Statement for Lookup

Don't do this

String getCaffeine(String type) {
  switch (type) {
    case 'Coffee':
      return '95 mg';
    case 'Redbull':
      return '147 mg';
    case 'Tea':
      return '11 mg';
    case 'Soda':
      return '21 mg';
    default:
      return 'Not found';
  }
}


void main() {
  print(getCaffeine('Coffee'));   // Output: 95 mg
  print(getCaffeine('Soda'));     // Output: 21 mg
  print(getCaffeine('Juice'));    // Output: Not found
}


Do this

String getCaffeine(String type) {
  const caffeineMap = {
    'Coffee': '95 mg',
    'Redbull': '147 mg',
    'Tea': '11 mg',
    'Soda': '21 mg',
  };

  return caffeineMap[type] ?? 'Not found';
}


void main() {
  print(getCaffeine('Coffee'));   // Output: 95 mg
  print(getCaffeine('Soda'));     // Output: 21 mg
  print(getCaffeine('Juice'));    // Output: Not found
}

Use of Null safe (??) Operator

Prefer using ?? (if null) and ?. (null aware) operators instead of null checks in conditional expressions. It reduces code and make code more cleaner.


Don't

var side = rightside == null ? leftside : rightside;
var side = rightside ?? leftside;


Do

var side = rightside ?? leftside; //If Null
var userName = user?.name; //Null aware

Use only relative imports for files in lib/

When create multiple files within our lib/ folder and import it in one another. Use of absolute and relative together can create confusion, To avoid this we should use relative imports for files.


Don't

import package:appname/utilities/ server_config.dart;


Do

import'./utilities/server_config.dart;

Use raw string

A raw string can be used to avoid escaping only backslashes and dollars.


Do

var s = r'This is demo string \ and $';


Don't

var s = 'This is demo string \\ and \$';

Use relative imports for files in lib

When use relative and absolute imports together then It is possible to create confusion when the same class gets imported from two different ways. To avoid this case we should use a relative path in the lib/ folder.


Do

import '../../../utils/dialog_utils.dart';


Don't

import 'package:demo/src/utils/dialog_utils.dart';