pub package Latest commit

A flutter package for custom widgets and utility function.

Migrate from 0.2.x to 0.3.x


    • remove FutureManager, AsyncSubjectManager, FutureManagerBuilder
    • All manager class now has a separate package
      sura_manager: ^1.5.0


Add this to pubspec.yaml

  sura_flutter: ^1.4.2


ConditionalWidgetBuild a widget base on a boolean condition
Divider0Divider with 0 height
EllipsisTextNullable Text with Ellipsis as default overflow
KeyboardDismissDismiss keyboard on tap
LoadingOverlayCreate an overlay loading that cover entire screen and disable input
LoadingOverlayPopScopeprevent or allow user from pop the screen when LoadingOverlay is displaying
SpaceXSizedBox with only width
SpaceYSizedBox with only height
SuraAccordionCustom ExpansionTile
SuraActionSheetCustom CupertinoActionSheet for option selector
SuraAsyncButtonFully customize Material ElevatedButton for asynchronous onPressed callback
SuraAsyncIconButtonSuraIconButton with asynchronous onPressed callback
SuraBadgeSmall badge like notification
SuraConfirmationDialogPlatform adaptive AlertDialog with cancel and confirm action
SuraExpandableSimilar to SuraAccordion but with different use case
SuraFlatButtonCustom TextButton or FlatButton
SuraFutureHandlerFutureBuilder with less boilerplate code
SuraIconButtonCustom IconButton
SuraListTileCustom ListTile
SuraLoadingDialogCreate and manage Loading Dialog, Deprecated and shouldn't be use. Consider using LoadingOverlay instead
SuraNotifierCustom ValueListenableBuilder
SuraPaginatedGridGridView with pagination support
SuraPaginatedListListView with pagination support
SuraPlatformCheckerPlatform adaptive widget
SuraProviderA provider for SuraFlutter global setting
SuraRaisedButtonCustom ElevatedButton with loading notifier
SuraSimpleDialogSimple platform adaptive AlertDialog
SuraStreamHandlerA StreamBuilder with less boilerplate code
SuraToolbarCustom ToolBar or AppBar
ValueNotifierWrapperWrapper with ValueNotifier when using StatelessWidget
WidgetDisposerProvide a dispose callback when using StatelessWidget



Create an override method that will call after the build method has been called

class _HomePageState extends State<NewPage> with AfterBuildMixin {

  //this method will call after widget has been build
  void afterBuild(BuildContext context) {


  Widget build(BuildContext context) {
    return Container();


Provide some property and method when working with Form

field and attribute

  • formKey: a key for form
  • loadingNotifier: a bool ValueNotifier
  • passwordObscureNotifier: a bool ValueNotifier for toggling password obscure field
  • isFormValidated: a bool return by validate formKey


  • toggleLoading: toggle loadingNotifier
  • togglePasswordObscure: toggle passwordObscureNotifier
class _HomePageState extends State<NewPage> with SuraFormMixin {
  Widget build(BuildContext context) {
    return Scaffold(
      body: Form(key: formKey, child: child)


Provider a ValueNotifier

  • boolNotifier: a bool ValueNotifier


  • toggleValue: toggle loadingNotifier
class _HomePageState extends State<NewPage> with BoolNotifierMixin {
  Widget build(BuildContext context) {
    return Container();


BuildContext extension

  Size screenSize = context.screenSize;
  Color primaryColor = context.primaryColor;
  Color accentColor = context.accentColor;
  TextThemeData textTheme = context.textTheme;
  Theme theme = context.theme;
  MediaQueryData data = context.mediaQuery;

TextStyle Extension

Text("Hello Flutter", style: TextStyle().normal)
Text("Hello Flutter", style: TextStyle().medium)
Text("Hello Flutter", style: TextStyle().bold)
Text("Hello Flutter", style: TextStyle().semiBold)
Text("Hello Flutter", style: TextStyle().white)
Text("Hello Flutter", style: TextStyle().black)
Text("Hello Flutter", style: TextStyle().red)
Text("Hello Flutter", style: TextStyle().green)
Text("Hello Flutter", style: TextStyle().grey)
Text("Hello Flutter", style: TextStyle().underline)
Text("Hello Flutter", style: TextStyle().setColor(Colors.white))
Text("Hello Flutter", style: TextStyle().setFontSize(24))

///This responsive font size is configure as following using SuraResponsive value:
/// tablet: value + 4
/// desktop: value + 6
/// small mobile: value - 2
Text("Hello Flutter", style: TextStyle().responsiveFontSize(24))

DateTime extension "dd mmm yyyy", locale: context.locale) "dd mmm yyyy", locale: context.locale)

List and map extension

///Filter list
List<int> adult = [2,24,12,18].filter((age)=> age >= 18);

///Add age to Map if age isn't null
Map<String, int> data = {};
int? age = 20;

///Return null if age doesn't exist

Widget's Extension

Text("Hello Flutter").padding(EdgeInsets.all(16.0)) // default value is EdgeInsets.all(8.0)
Text("Hello Flutter").margin(EdgeInsets.all(16.0)) // default value is EdgeInsets.all(8.0)
///As a value
Text("Hello Flutter").marginValue(all: 12)
Text("Hello Flutter").paddingValue(horizontal: 12, vertical: 8)
Text("Hello Flutter").cssSpacing(margin: [10,10], padding:[16])
//css margin and padding rule
Text("Hello Flutter").rotate(45)
///Rotate 45 degree
Text("Hello Flutter").flexible
Text("Hello Flutter").expanded
Text("Hello Flutter").clipOval
Text("Hello Flutter").opacity(0.5)

String extension

String name = "chunlee".capitalize() // => Chunlee

Utility and Style


alt text

      indicator: DotTabIndicator(
        dotAlignment: TabAlignment.bottom,


alt text

      isScrollable: true, //This indicator work best with scrollable tab bar
      indicator: SmallUnderlineTabIndicator(
        paddingLeft: 16,
        alignment: TabAlignment.bottom,


This input border solve a problem that TextField doesn't have a default elevation.

alt text

      decoration: InputDecoration(
        border: ShadowInputBorder(
          elevation: 2.0, //required
          fillColor: Colors.white, //required
          borderRadius: SuraDecoration.radius(),
          shadowColor: Colors.black87,



//Get Color from hex string
Color green = SuraColor.fromCode("42f545")

//Get Color from RGB without Alpha or Opacity
Color newColor = SuraColor.fromRGB(8, 182, 155)

//Convert color to MaterialColor
MaterialColor newMaterialColor = SuraColor.toMaterial(0xFF869CF4)


//Ping to google to check for internet connection
bool isConnected = await SuraUtils.checkConnection();

//Convert degree to radian value
double radian = SuraUtils.degreeToRadian(90);

//Future.delayed base on millisecond value
await SuraUtils.wait(200);

//Get random image from unsplash
String carUrlImage =  SuraUtils.unsplashImage(width: 200, height: 200, category: "car");

//Get byte from asset bundle
Future<Uint8List> imageByte = await SuraUtils.getBytesFromAsset("image asset path", 200); //200 is an image width

//Get random image from unsplash
String carUrlImage =  SuraUtils.unsplashImage(width: 200, height: 200, category: "car");

//Get random from picsum with provided: width and height
String randomUrlImage = SuraUtils.picsumImage(200,300);


Provide some field validation

  validator: (value) => SuraFormValidator.validateField(value, field: "username"),

  validator: (value) => SuraFormValidator.isNumber(value, field: "age"),

  validator: (value) => SuraFormValidator.validateEmail(value, field: "email"),

SuraPageNavigator and SuraNavigator

PageNavigator support push, pushReplacement and pushAndRemove method

///use name param to provide RouteSetting's routeName
SuraPageNavigator.push(context, DetailPage(), name: "detail-page");

///Also support RouteSetting
SuraPageNavigator.pushReplacement(context, HomePage(), settings: RouteSetting());

///Remove all
SuraPageNavigator.pushAndRemove(context, RootPage());

SuraNavigator also support push, pushReplacement, pushAndRemove without providing a context but you need to add SuraNavigator.navigatorKey to MaterialApp

    navigatorKey: SuraNavigator.navigatorKey,
    home: MyHomePage(),

SuraNavigator also can show dialog without providing a context

var result = await SuraNavigator.dialog(MyDialog());


RoundedRectangleBorder roundRectangle = SuraDecoration.roundRect(12); //default value is 8
BorderRadius radius = SuraDecoration.radius(12); //default value is 8


A responsive tool to help define a value base on screen size

  • Wrap your Home widget in MaterialApp with SuraResponsiveBuilder


// Only required first parameter
//set value 20 for mobile size
//set value 24 for tablet size
//set value 28 for desktop size
//set value 16 for small mobile size
//If context isn't null, it will react to MediaQuery change
double width = SuraResponsive.value(20,24,28,16,context);

///Auto value base on provided rule
///-4 for small phone, +8 for tablet and +16 for Desktop if using add rule
///-25% for small phone, x2 for tablet and x3 for Desktop if using multiply rule
double width =,SuraResponsiveRule.add);

Widget child = SuraResponsive.builder(
  mobile: ()=> MobileWidget(), ///required
  tablet: ()=> TabletWidget(), ///required
  desktop: ()=> DesktopWidget(), ///Optional, using tablet widget if value is null
  mobileSmall: ()=> MobileSmallWidget(), ///Optional, using mobile widget if value is null