Flutter - Other

Logging with Log Function

The default print function limits the number of characters shown in the console. For better logging, use the log function, which captures and displays much larger amounts of data, making debugging easier.

log('This is a log message');

Multidex issue with Flutter

Enable multidex

Open [project_folder]/app/build.gradle and add following lines.

defaultConfig {
    ...

    multiDexEnabled true
}

dependencies {
    ...

    implementation 'com.android.support:multidex:1.0.3'
}


Enable Jetifier

Open [project_folder]/android/gradle.properties and add following lines.

android.useAndroidX=true
android.enableJetifier=true

Naming convention

Classes, enums, typedefs, and extensions name should in UpperCamelCase.

class MainScreen { ... }
enum MainItem { .. }
typedef Predicate<T> = bool Function(T value);
extension MyList<T> on List<T> { ... }


Libraries, packages, directories, and source files name should be in snake_case(lowercase_with_underscores).

library firebase_dynamic_links;
import 'socket/socket_manager.dart';


Variables, constants, parameters, and named parameters should be in lowerCamelCase.

var item;
const bookPrice = 3.14;
final urlScheme = RegExp('^([a-z]+):');
void sum(int bookPrice) {
  // ...
}

Never Have More Than 3 Arguments

Do

printUserInfo(User user) => print('${user.name} ${user.phone} ${user.email} ${user.address}');


Don't

printUserInfo(String name, String phone, String email, String address) {
  print(name + phone + email + address);
}


Tips:

  1. When a function seems to need more than two or three arguments, it is likely that some of those arguments ought to be wrapped into a class of their own.
  2. Avoid use "+" to concatenate String. Example: name + phone -> '$name $phone'

Not Connecting to IOS Simulator in Android Studio

Please check whether you installed the latest version of Xcode.


After the installation, try to run the following command.

sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer

Open android module in Android Studio is disabled

  1. Create a new dummy project in Flutter.
  2. Go to ..\android\gradle.
  3. Copy the .iml file from dummy project to the current project.

Optimize image

import 'package:image/image.dart' as img;

Future<List<int>> optimizeImage(String imagePath) async {
  final bytes = await rootBundle.load(imagePath);
  final image = img.decodeImage(bytes.buffer.asUint8List());
  final optimizedImage = img.encodePng(img.copyResize(image, width: 800));
  return optimizedImage.toList();
}

Overloading the Main Thread with Heavy Operations

Blocking the main thread by running heavy operations on it can lead to frozen UIs and frustrated users. For example, reading a large file directly on the main thread is a recipe for disaster:

void readLargeFile() {
  final file = File('large_file.txt');
  final contents = file.readAsStringSync(); // Blocking operation
  print(contents);
}


Instead, you can offload this operation to a background thread using Flutter's compute() function:

Future<void> readLargeFile() async {
  final contents = await compute(_readFile, 'large_file.txt');
  print(contents);
}

String _readFile(String path) {
  final file = File(path);
  return file.readAsStringSync();
}


This way, you keep the UI thread responsive while performing the heavy file operation in the background.

Pad Left and Right a String

padLeft

String padLeft(int width, [String padding = ' ']);

String s = "woolha";

print(s.padLeft(10)); 
//'    woolha'

print(s.padLeft(10, '.')); 
//'....woolha'

print(s.padLeft(10, ',.*'));
//',.*,.*,.*,.*woolha'

print(s.padLeft(3, '.'));
//'woolha'


padRight

String padRight(int width, [String padding = ' ']);

String s = "woolha";

print(s.padRight(10));  
//'woolha    '

print(s.padRight(10, '.'));
//'woolha....' 

print(s.padRight(10, ',.*')); 
//'woolha,.*,.*,.*,.*'

print(s.padRight(3, '.'));
//'woolha'


What if string is null

String x = null;
print(x?.padLeft(10, '.')); 
//null

Powerful of Enums in dart

Do

class _HomeScreenState extends State<HomeScreen> {
  LoadingState state = LoadingState.complete;

  @override
  Widget build(BuildContext context) {
    switch (state) {
      case LoadingState.stopped:
        return Text(state.name);
      case LoadingState.loading:
        return Text(state.name);
      case LoadingState.complete:
        return Text(state.name);
      case LoadingState.failed:
        return Text(state.name);
    }
  }
}


Don't

class _HomeScreenState extends State<HomeScreen> {
  LoadingState state = Stopped();

  @override
  Widget build(BuildContext context) {
    if(state is Stopped) {
      return const Text("Stopped");
    }
    else if(state is Loading) {
      return const Text("Loading");
    }
    else if(state is Complete) {
      return const Text("Complete");
    }
    else if(state is Failed) {
      return const Text("Failed");
    }
    else {
      return const Text("");
    }
  }
}


Conclusion: If you used classes extends from LoadingState, it would be hard to discover what the other states (which the constants in enum could be used to define) are. However with an enum, even a simple switch statement will complain if you don't fill in all the cases. There are a lot of things to like about the Enums. Please look into flutter documentation.

Preserving state for TabView or PageView pages

Called AutomaticKeepAliveClientMixin in your State class.


class _SearchScreenState extends State<SearchScreen> with AutomaticKeepAliveClientMixin<SearchScreen>{

...

  @override
  Widget build(BuildContext context) { 
    // call this method
    super.build(context); 

    /// your widget here
  }

  @override
  bool get wantKeepAlive => true;

}

Prevent Video Recording & Screenshot

Flutter

  1. Add flutter_windowmanager to pubspec.yaml.
  2. Add the code below. If needed to apply on complete app, then call it in main.dart file.
Future<void> secureScreen() async {
  await FlutterWindowManager.addFlags(FlutterWindowManager.FLAG_SECURE);
}

@override
void initState() {
  secureScreen();
  super.initState();
}


Android

  1. Locate MainActivity file and import Android.view.windowmanager.layoutparams.
  2. Add the code below.
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.oCreate(savedInstanceState);

  getWindow().addFlags(LayoutParams.FLAG_SECURE);
}


ios

  1. Locate Appdelegate.m file and add the code below.
@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)LaunchOptions {
  [GeneratedPluginRegistrant registerWithRegistry:self];
  //Override point for customization after application launch.
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

- (void)applicationWillResignActive:(UIApplication *)application {
    self.window.hidden = YES;
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    self.window.hidden = NO;
}

@end