Use spread collections
When existing items are already stored in another collection, spread collection syntax leads to simpler code.
Do
var y = [4,5,6]; var x = [1,2,...y];
Don't
var y = [4,5,6]; var x = [1,2]; x.addAll(y);
class someAPI { Future<int> getThings() => Future.value(3000); Future<int> getItems() => Future.value(300); Future<int> getStuff() => Future.value(30); } final api = someAPI(); final values = await Future.wait( [ api.getThings(), api.getItems(), api.getStuff() ] );
Flexible( child: GridView.count( crossAxisCount: (orientation == Orientaation.portrait) ? 2 : 3, mainAxisSpacing: 4.0, crossAxisSpacing: 4.0, padding: const EdgeInsets.all(4.0), childAspectRatio: (orientation == Orientaation.portrait) ? 1.0 : 1.3, children: someList.map( (catData) => aListItemWidget(catData) ).toList() ) );
Widget to the list of children, you will need to change the height of the Sizedbox whenever you add a widget.
Don't
SizedBox( height: 20, child: Column( children: [ Text(Hey), Text(You) ] ) )
Do
Column( mainAxisSize: MainAxisSize.min, child: Column( children: [ Text(Hey), Text(You) ] ) )
You can use the print0 function to view it in the system console. If your output is too much, then Android sometimes discards some log lines. To avoid this, you can use debugPrint().
You can also log your print calls to disk if you're doing long-term or background work.
Just wrap the widget with the Theme Widget and pass the ThemeData().
Theme( data: ThemeData(...), child: TextFormField( decoration: const InputDecoration( icon: Icon(Icons.person), hintText: 'What do people call you ?', labelText: 'Name *', ) validator: (value) { return value!.contains('@') ? 'Do not use the @ char' : null; } ) )
allprojects { repositories { google() mavenCentral() } tasks.withType(JavaCompile).configureEach { javaCompiler = javaToolchains.compilerFor { languageVersion = JavaLanguageVersion.of(8) } } }
initialSettings: InAppWebViewSettings(contentBlockers: [ ContentBlocker( trigger: ContentBlockerTrigger( urlFilter: ".*", resourceType: [ ContentBlockerTriggerResourceType.IMAGE, ContentBlockerTriggerResourceType.STYLE_SHEET ], unlessDomain: ["example.com", "github.com", "pub.dev"] ), action: ContentBlockerAction( type: ContentBlockerActionType.BLOCK ) ) ]),
For deeper trigger customization, you can use the other properties of ContentBlockerTrigger :
initialSettings: InAppWebViewSettings(contentBlockers: [ ContentBlocker( trigger: ContentBlockerTrigger( urlFilter: "https://flutter.dev/.*", ), action: ContentBlockerAction( type: ContentBlockerActionType.CSS_DISPLAY_NONE, selector: '.notification, .media, #developer-story' ) ) ]),
Valid types are:
import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); if (!kIsWeb && kDebugMode && defaultTargetPlatform == TargetPlatform.android) { await InAppWebViewController.setWebContentsDebuggingEnabled(kDebugMode); } runApp(const MaterialApp(home: MyApp())); } class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { final GlobalKey webViewKey = GlobalKey(); // list of Ad URL filters to be used to block ads loading. final adUrlFilters = [ ".*.doubleclick.net/.*", ".*.ads.pubmatic.com/.*", ".*.googlesyndication.com/.*", ".*.google-analytics.com/.*", ".*.adservice.google.*/.*", ".*.adbrite.com/.*", ".*.exponential.com/.*", ".*.quantserve.com/.*", ".*.scorecardresearch.com/.*", ".*.zedo.com/.*", ".*.adsafeprotected.com/.*", ".*.teads.tv/.*", ".*.outbrain.com/.*" ]; final List<ContentBlocker> contentBlockers = []; var contentBlockerEnabled = true; InAppWebViewController? webViewController; @override void initState() { super.initState(); // for each Ad URL filter, add a Content Blocker to block its loading. for (final adUrlFilter in adUrlFilters) { contentBlockers.add(ContentBlocker( trigger: ContentBlockerTrigger( urlFilter: adUrlFilter, ), action: ContentBlockerAction( type: ContentBlockerActionType.BLOCK, ))); } // apply the "display: none" style to some HTML elements contentBlockers.add(ContentBlocker( trigger: ContentBlockerTrigger( urlFilter: ".*", ), action: ContentBlockerAction( type: ContentBlockerActionType.CSS_DISPLAY_NONE, selector: ".banner, .banners, .ads, .ad, .advert"))); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Ads Content Blocker"), actions: [ TextButton( onPressed: () async { contentBlockerEnabled = !contentBlockerEnabled; if (contentBlockerEnabled) { await webViewController?.setSettings( settings: InAppWebViewSettings( contentBlockers: contentBlockers)); } else { await webViewController?.setSettings( settings: InAppWebViewSettings(contentBlockers: [])); } webViewController?.reload(); setState(() {}); }, style: TextButton.styleFrom(foregroundColor: Colors.white), child: Text(contentBlockerEnabled ? 'Disable' : 'Enable'), ) ], ), body: SafeArea( child: Column(children: <Widget>[ Expanded( child: Stack( children: [ InAppWebView( key: webViewKey, initialUrlRequest: URLRequest(url: WebUri('https://www.tomshardware.com/')), initialSettings: InAppWebViewSettings(contentBlockers: contentBlockers), onWebViewCreated: (controller) { webViewController = controller; }, ), ], ), ), ]))); } }
Do
@override Widget build(BuildContext context) { return Scaffold( body: Column( children: const [ Widget(), Widget(), Widget() ] ) ); }
Don't
@override Widget build(BuildContext context) { return Scaffold( body: Column( children: [ WidgetFunction(), WidgetFunction(), WidgetFunction() ] ) ); }
Conclusion: Extracting widgets to a method is considered as a Flutter anti-pattern, because when Flutter rebuilds widget tree, it calls the function all the time, making more processor time for the operations while widgets they will be rendered once and will not update themselves.
Understanding the widget lifecycle is essential for effective Flutter development. The WidgetsBinding mixin facilitates the management of this lifecycle through various methods. Let's explore some key methods that the mixin introduces:
initState()
The initState() method is called when a StatefulWidget is inserted into the widget tree for the first time. It provides an opportunity to initialize the state of the widget, making it a suitable place for tasks like data fetching or setting up event listeners.
@override void initState() { super.initState(); // Initialize state here }
The build() method is where the UI of the widget is constructed. It is invoked whenever the widget is rebuilt, allowing developers to create a dynamic and responsive user interface.
@override Widget build(BuildContext context) { return // Your UI components here }
The dispose() method is called when the widget is removed from the widget tree. Developers can use this method to perform cleanup tasks such as releasing resources or canceling subscriptions.
@override void dispose() { // Clean up resources here super.dispose(); }
The didChangeAppLifecycleState method is invoked whenever the application's lifecycle state changes. This includes transitioning between the foreground and background, providing an opportunity to pause or resume tasks as needed.
@override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.resumed) { // App resumed, reactivate tasks } else if (state == AppLifecycleState.paused) { // App paused, deactivate tasks } }
This method is called when the platform brightness changes, allowing developers to adapt their UI based on changes in ambient light conditions.
@override void didChangePlatformBrightness() { // Adjust UI based on brightness }
The didUpdateWidget method is invoked whenever the framework replaces an old widget with a new one. This occurs when the parent widget rebuilds and creates a new instance of the widget. This method is beneficial for responding to changes in widget properties.
@override void didUpdateWidget(MyWidget oldWidget) { // React to changes in widget properties super.didUpdateWidget(oldWidget); }
The reassemble method is called during hot-reloading, providing developers with an opportunity to update the UI or reset state without restarting the application.
@override void reassemble() { // Perform actions for hot-reloading super.reassemble(); }
Flutter’s smooth animations and delightful user interfaces are often attributed to its advanced rendering pipeline. The WidgetsBinding mixin provides methods that play a pivotal role in orchestrating frame scheduling and handling animations.
The scheduleFrameCallback method allows you to register a callback that will be invoked before the next frame is rendered. This is particularly useful for orchestrating complex animations and interactions.
void myAnimationCallback(Duration timeStamp) { // Update animation state here } void scheduleAnimation() { WidgetsBinding.instance?.scheduleFrameCallback(myAnimationCallback); }
The addPostFrameCallback method schedules a callback to be invoked after the frame has been rendered. This is beneficial when you need to perform actions after the UI has settled.
void myPostFrameCallback(Duration timeStamp) { // Perform actions after the frame has been rendered } void schedulePostFrameCallback() { WidgetsBinding.instance?.addPostFrameCallback(myPostFrameCallback); }
Beyond managing the widget lifecycle and animations, the WidgetsBinding mixin provides hooks for handling user interaction and system events.
The handlePointerEvent method allows you to intercept and handle low-level pointer events. This can be useful for implementing custom gestures or interactions.
void handlePointerEvent(PointerEvent event) { // Handle pointer events } WidgetsBinding.instance?.handlePointerEvent = handlePointerEvent;
The handlePopRoute method is called when the system requests to pop the current route. This can be useful for handling back button presses.
@override Future<bool> handlePopRoute() async { // Handle the pop route request return true; // Return true if the route is popped }