Top useful Flutter packages grouped by feature

Posted on December 18th, 2022

With a certain feature, there can be many packages for you to choose from. How to find the right library that implements quickly and has few bugs. I will categorize them based on work experience.

1. Flutter PDF Viewer

Pros:

Hyperlink handle

Cons:

Apk size is too large.

Setup:

PDFView(
  pageFling: false,
  pageSnap: false,
  filePath: _remotePDFpath,
  onRender: (_) {
   // Stop loading
  },
  // onError, onPageError (Android only)
  // https://github.com/endigo/flutter_pdfview/blob/780a3d30e27c6a0610f9ad74630d6155fe4ee355/CHANGELOG.md#1005
  onError: (error) {
    debugPrint("onError ${error.toString()}");
    _handleError();
  },
  onPageError: (page, error) {
    debugPrint("onPageError ${error.toString()}");
    _handleError();
  },
  preventLinkNavigation: true,
  onLinkHandler: (uri) {
    debugPrint("onLinkHandler $uri");
    if (uri == null || uri.isEmpty) return;
    launchUrl(Uri.parse(uri), mode: LaunchMode.platformDefault);
  },
  onViewCreated: (PDFViewController controller) {
    if (Platform.isIOS) {
      // fix: init scroll top
      Future.delayed(const Duration(milliseconds: 50), () {
        controller.setPage(0);
      });
    }
  },
  autoSpacing: Platform.isIOS,
),

Download file:

Future<File?> _createFileOfPdfUrl(String url) async {
  try {
    var res = await http.get(Uri.parse(url));
    if (res.statusCode != 200 || res.body.startsWith("<?xml version=") || res.body.startsWith("<!DOCTYPE html")) {
      throw Exception('statusCode ${res.statusCode}_${res.reasonPhrase}');
    }
    final filePath = await _getPdfFilePath;
    File file = File(filePath);
    await file.writeAsBytes(res.bodyBytes, flush: true);
    return file;
  } catch (e) {
    debugPrint(e.toString());
    return null;
  }
}

2. Flutter Webview

Pros:

Official webview lib of Flutter

Cons:

  • Few platform-specific features

- No singleton mode in webview_flutter iOS

- Android setTextZoom, setMinimumFontSize

Add below code in pubspec.yaml to apply path fix

dependency_overrides:
  webview_flutter_android:
    git:
      url: https://github.com/guide-flutter/plugins.git
      path: packages/webview_flutter/webview_flutter_android
      ref: webview_flutter_android
  webview_flutter_wkwebview:
    git:
      url: https://github.com/guide-flutter/plugins.git
      path: packages/webview_flutter/webview_flutter_wkwebview
      ref: webview_flutter_wkwebview

Parse cookies in headers of http response

String _getSetCookie(final Map<String, dynamic> headers) {
  for (final key in headers.keys) {
    // システムによって返却される "set-cookie" のケースはバラバラです。
    if (key.toLowerCase() == 'set-cookie') {
      return headers[key] as String;
    }
  }

  return '';
}
/// The regex pattern for splitting the set-cookie header.
final _regexSplitSetCookies = RegExp(
  r"""(?<!expires=\w{3}|"|')\s*,\s*(?!"|')""",
  caseSensitive: false,
);
List<Cookie> fromCookiesString(String? cookiesString) {
  final cookieList = cookiesString?.split(_regexSplitSetCookies) ?? [];
  final List<Cookie> cookies = [];
  for (var e in cookieList) {
    try {
      final cookie = Cookie.fromSetCookieValue(e);
      cookie.domain ??= Constants.DomainUrl; // need to set domain if cookie.domain = null
      cookies.add(cookie);
      debugPrint("__cookie ${cookie.name}:${cookie.secure}");
    } catch (e) {
      debugPrint(e.toString());
    }
  }
  return cookies;
}

Set cookies to Webview

Need to use third package: webview_cookie_manager

Future<void> _syncCookies(List<Cookie> cookies) async {
  final cookieManager = WebviewCookieManager();
  if (Platform.isAndroid) {
    // Clear old cookies avoid duplication, (iOS no need & iOS < 12.4 hangs app with clearCookies)
    await cookieManager.clearCookies();

    // https://pub.dev/packages/webview_cookie_manager#secure-attribute
    final gCookieDomain = groupBy(cookies, (c) => c.domain);
    for (var domain in gCookieDomain.keys) {
      final cks = gCookieDomain[domain] ?? [];
      final gCookieSecure = groupBy(cks, (c) => c.secure);

      for (var isSecure in gCookieSecure.keys) {
        final ckks = gCookieSecure[isSecure]?.toList() ?? [];
        if (ckks.isNotEmpty) {
          debugPrint("_______Cookie: ${ckks.toString()}");
          await cookieManager.setCookies(ckks, origin: isSecure ? 'https://$domain' : null);
        }
      }
    }
  } else {
    await cookieManager.setCookies(cookies);
  }
}

3. Flutter Jailbreak or Root detection

Path fix:

flutter_jailbreak_detection:
  git:
    url: https://github.com/guide-flutter/flutter_jailbreak_detection.git

4. Flutter Gen app icon

Config yaml

# gen app icon
flutter_icons:
  android: true
  image_path_ios: "assets/icon/icon_ios.png"
  image_path_android: "assets/icon/icon_android.png"
  min_sdk_android: 23
  adaptive_icon_background: "#ffffff"
  adaptive_icon_foreground: "assets/icon/icon_adaptive_foreground.png"

  ios: true
  remove_alpha_ios: true

5. Flutter UI display a reasonable layout on different screen sizes

Warning: Do not use ScreenUtilInit for individual pages, this will raise a bug if the page has an input text field

import 'dart:ui' as ui;


Widget build(BuildContext context) {
  return ScreenUtilInit(
    designSize: Size(375, 812 + MediaQueryData.fromWindow(ui.window).padding.bottom),
    minTextAdapt: true,
    builder: (BuildContext context, Widget? child) {
      return MaterialApp.router(
        builder: (context, child) {
          return MediaQuery(
            // Setting font does not change with system font size
            data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0, boldText: false),
            child: child ?? const SizedBox.shrink(),
          );
        },
      );
    },
  );