[Flutter]Define a Global BuildContext & Its Usage

*This post is more like a note to myself.

It’s simple, just add static keyword when defining a BuildContext.

It’s useful when you want to display a Toast or SnackBar in global context, or update an UI, the data will update, not only within one context (context of current page or exited or popped page and so on) but also in other contexts.

For example:

//To define global context
class AppConstants {
  static BuildContext kHomeContext;
}

To use this context:

Future.delayed(Duration(milliseconds: 300), () {
//delay 0.3 seconds to show the Toast
  ToastHelper.showToast(
    AppConstants.globalContext,//global context for the toast
    
    //global context for the language data to update
    L.of(AppConstants.globalContext).settings_languageChanged,
    hasIcon: true,
    isCrossPage: true,
  );
});

Flutter: Easy Way to Disable or Enable a Button

I spent hours on implementing disabling and enabling a button (Material button, raised button, outline button and so on) below a list of Radio group with setState({}) and Shared Preference, in order to save the value of isPressed variable permanently, I even tried with FutureBuilder and Bloc

The purpose I was trying to accomplish is the button should be disabled once it’s pressed, and enabled when new radio selection is made.

The premise of this implementation is to save the radio selection state with setState({}), you can save the selection state or value permanently by using Shared Preferences or similar methods, please see Dart Flutter: Custom Radio Button – Save Selection State with SharedPreferences-Like Methods.

Well, the radio selection state and value are all saved, now we can work on the button.

In any Flutter buttons, for example, RaisedButton, FlatButton, OutlinedButton, MaterialButton, and so on, there’s always an onPressed field, if you make this field null, the button will be disabled, and vice-versa.

So how to make it null?

In your button widget (in the class that extends StatefulWidget), create a bool value called enabled or isEnabled with final. And this bool value will receive parameter from the page that contains the button widget. Initialize the bool value with false. Code will be look like this:

class RectangleButton extends StatefulWidget {
  final VoidCallback onPressedCallback;
  final bool isEnabled;

  RectangleButton({
    ...
    this.isEnabled = false,
    this.onPressedCallback,
    ...
  });

  @override
  State<StatefulWidget> createState() {
    return _RectangleButton();
  }
}

Then, in your button widget state class (class extends State<[StatefulWidgetClass]>), go to onPressed field of the button widget (MaterialButton in this case), make a if statement to check if button is enabled, run method in the onPressedCallback(){}, otherwise, disable the button, the code could be like:

onPressed:widget.isEnabled ? widget.onPressedCallback : null,

And if you want to change color when the button became disabled, just add isEnabled to the disabledColor field of the MaterialButton() widget:

disabledColor: widget.isEnabled ? null : theme.disableColor,

So the code for MaterialButton will be something like this:

MaterialButton(
   disabledColor: widget.isEnabled ? null : theme.disableColor,
   textColor: Colors.white,
   padding: EdgeInsets.all(8.spx),
   child: Text(
     widget.text,
     style: textStyle,
   ),
   onPressed:widget.isEnabled ? widget.onPressedCallback : null,
),

Now, let’s get to the page that implements this widget.

Create a bool value that’s called _btnEnabled, assign this local variable with false by default.

In the setState({}) that contains the radio selection state switch logic, put the _btnEnabled variable before any radio selection change, and assign true to it.

bool _btnEnabled = false;
...
onTap(){
  setState(() {
    _btnEnabled = true;
    /* logic for saving temporary radio selection sate */
  });
}

And finally, add _btnEnabled to the widget implemented in the page:

RectangleButton(
  width: double.infinity,
  height: 50.spx,
  text: locale.login_buttonSave,
  isEnabled: _btnEnabled,
  onPressedCallback: _btnEnabled ? () {
    //save the radio selection value and state, and refresh the page
  }: null,
),

Dart Flutter: Custom Radio Button – Save Selection State with SharedPreferences-Like Methods

First, create a custom radio button with a RadioButtonModel class, UI only.

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';

class RadioGroup extends StatefulWidget {
  final RadioButtonModel _item;

  RadioGroup(this._item);

  @override
  State<StatefulWidget> createState() {
    return _RadioGroup();
  }
}

class _RadioGroup extends State<RadioGroup>{

  AppTheme get theme{
    return AppOptions().theme;
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 55.spx,
      padding: EdgeInsets.symmetric(horizontal: 20.spx),
      alignment: Alignment.centerLeft,
      child: Row(
        mainAxisSize: MainAxisSize.max,
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
          Text(
            widget._item.text,
            style:
                AppStyle().textRegularsth.copyWith(color: theme.mainTextColor),
          ),
          Container(
            child: () {
              if (widget._item.isSelected) {
                return SvgPicture.asset(
                  "assets/images/svg/icon_check_blue.svg",
                  color: theme.accentColor,
                  width: 18.spx,
                  height: 18.spx,
                );
              }
              return SizedBox();
            }(),
          ),
        ],
      ),
    );
  }
}
class RadioButtonModel{
  bool isSelected;
  final String text;
  final Locale locale;
  final String buttonIcon;

  RadioButtonModel(this.isSelected, this.text, this.locale, {this.buttonIcon});
}

Language page:

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_localized_locales/flutter_localized_locales.dart';

class PageLanguage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _PageLanguageState();
  }
}

class _PageLanguageState extends BasePageState<PageLanguage> {
  String _lang = "en";
  bool _isSelected = false;

  List<Locale> _langKeyList = [];
  List<String> _translatedLangList = [];
  List<RadioButtonModel> langRadioList = [];
  Map<String, String> _langNativeNamesList;

  @override
  void onCreate(BuildContext context) {
    super.onCreate(context);
    _getLangCodeListStr();
    _getLangNamesList();
    _lang = MemoryKeyValueStore().getString("lang");
    for (int i = 0; i < _translatedLangList.length; i++) {
      langRadioList.add(
        RadioButtonModel(
          _lang == _langKeyList[i].toString(),
          _translatedLangList[i],
          _langKeyList[i],
        ),
      );
    }
  }

  @override
  Color backgroundColor() {
    return theme.backgroundColor;
  }

  @override
  Widget appBar(BuildContext context) {
    return AppBar(
      backgroundColor: theme.backgroundColor,
      elevation: 0,
      title: Text(
        locale.settings_language,
        style: AppStyle().textRegularsth.copyWith(
          color:theme.mainTextColor, fontSize: 17.spx,
          fontWeight:FontWeight.w700),
      ),
      centerTitle: true,
      leading: SimpleBackButton(
        color: theme.mainTextColor,
        width: 16.spx,
        height: 18.spx,
      ),
      bottom: PreferredSize(
        preferredSize: Size.fromHeight(theme.dividerAndBorderWidth),
        child: Container(
          height: theme.dividerAndBorderWidth,
          color: theme.lineColor,
        ),
      ), //temporary
    );
  }

  @override
  Widget body(BuildContext context) {
    return Container(
      height: 10.spx,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          Container(
              color: theme.disableColor,
              child: Padding(
                padding: EdgeInsets.all(20.spx),
                child: Text(
                  locale.settings_langPageLegend,
                  style: AppStyle()
                      .linkFontRegular
                      .copyWith(color: theme.secondaryTextColor),
                ),
              )),
          SizedBox(height: 20.spx),
          Expanded(
            child: ListView.separated(
              shrinkWrap: true,
              itemCount: langRadioList.length ?? 0,
              itemBuilder: (BuildContext context, int index) {
                return InkWell(
                  onTap: () {
                    var item = langRadioList[index];
                    languageChanged(item.locale, index);
                  },
                  child: RadioGroup(langRadioList[index]),
                );
              },
              separatorBuilder: (BuildContext context, int index) {
                return kLine(
                    thickness: 0.35.spx,
                    color: theme.lineColor,
                    padding: EdgeInsets.symmetric(horizontal: 20.spx));
              },
            ),
          ),
          Container(
            padding: EdgeInsets.only(
                top: 18.spx, bottom: 18.spx, left: 20.spx, right: 20.spx),
            child: RectangleButton(
              width: double.infinity,
              height: 50.spx,
              text: locale.login_buttonSave,
              hasItemSelected: _isSelected,
              onPressedCallback: () {
                if (_lang != MemoryKeyValueStore().getString("lang")) {
                  MemoryKeyValueStore().putString("lang", _lang);
                  optionsUpdate();
                  setState(() {});
                }
                NavigationMaster().account.goBack();
                DialogBuilder.showToast(context, locale.settings_languageChanged,showIcon: true);
              },
            ),
          ),
        ],
      ),
    );
  }

  List<String> _getLangNamesList() {
    _langNativeNamesList = LocaleNamesLocalizationsDelegate.nativeLocaleNames;
    for (var locale in _langKeyList) {
      _translatedLangList.add(_langNativeNamesList[locale.toString()]);
    }
    /* _translatedLangList.sort((a, b) {
      return a.compareTo(b);
    });*/
    return _translatedLangList;
  }

  List<String> _getLangCodeListStr() {
    _langKeyList = L.supportedLocales();
    List<String> langCodeStrList = [];
    for (var lanCode in _langKeyList) {
      langCodeStrList.add(lanCode.toString());
    }
    /*langCodeStrList.sort((a, b) {
      return a.compareTo(b);
    });*/
    return langCodeStrList;
  }

  bool isSavingLanguageChanges() {
    return false;
  }

  Widget _showLinearProgressBar(bool isInProgress) {
    if (isInProgress) {
      return kGtvLinearProgressBar(context);
    } else {
      return PreferredSize(
        preferredSize: Size.fromHeight(theme.dividerAndBorderWidth),
        child: Container(
          height: theme.dividerAndBorderWidth,
          color: theme.lineColor,
        ),
      );
    }
  }

  String languageChanged(Locale locale, int index) {
    if (_lang != locale.toString()) {
      _lang = locale.toString();
      setState(() {
        langRadioList.forEach((element) => element.isSelected = false);
        langRadioList[index].isSelected = true;
        _isSelected = langRadioList[index].isSelected;
      });
    }
    return _lang;
  }

  void optionsUpdate() {
    AppOptions.updateWithLocale(context, AppOptions.langToLocale(_lang));
  }
}

How to Generate A Java Keystore for Android Apps

This tutorial focuses on how to generate Java keystores for Android Apps.

What you need:

  • A Bash/Shell (Git Bash works too), CMD, or any Terminal
  • Java JDK installed, and Java environment set

There are 3 types of keystores you can generate for your Android application:

  • The keystore for signing Android apps
  • The release keystore
  • The debug keystore

The following steps will tell you how to create RSA keys and you will receive a keystore that “stores” several private keys (Pay attention to different command environment when running the commands, for example, in Windows, we use “\” for path, and in Linux, we use ‘/” for path).

Continue reading “How to Generate A Java Keystore for Android Apps”

To Generate JKS Keystore From Existing Keystore & Use it in Android Apps

Note: This tutorial only works when you already have a keystore.

First, let’s generate a JKS (Java KeyStore, a repository of security certificates) file using an already existing keystore

  • Go to the location where your keystores are; or open CMD, use cd [path to the keystore(s)] to go to the location.
  • In this folder, on the CMD, or Gitbash, or other Terminals, type following command (for keytool.exe, see Facebook API: Key Hash for Android App):
"C:\Program Files (x86)\Java\jre1.8.0_251\bin\keytool" -importkeystore -srckeystore [keystore] -destkeystore [keystore.jks] -alias [alias_name] -deststoretype JKS
  • Enter the new password for the jks keystore, and enter again to confirm the password.
  • Enter your keyStore password (storePassword) for your original keystore (anykeystore.keystore for example), and enter again to confirm the password.
Continue reading “To Generate JKS Keystore From Existing Keystore & Use it in Android Apps”

Facebook API: Key Hash for Android App

Some said you can find the keytool in .android folder. You can go to that folder by running CMD (Command Prompt) in Windows, give the path for .android folder

cd PATH_WHERE_.android_LOCATED for example: C:\Users\USER_NAME\.android

But in some other cases, keytool.exe is not in the .android, but in JDK’s bin folder. It’s better to specify the path to the executable keytool (keytool.exe) as it could be anywhere.

Find the Java’s jre[version]\bin\keytool path in your local, it’s usually located in C:\Program Files(x86)\ (it could be different depend on your local disk), in this folder, copy paste the following command structure:

"C:\Program Files (x86)\Java\jre1.8.0_251\bin\keytool" -exportcert -alias androiddebugkey -keystore "[OPEN_LIBRARY_PATH]" | 
 "[OPEN_LIBRARY_PATH]" sha1 -binary | "C:\openssl-0.9.8k_X64\bin\openssl" base64

As for the Open Library path, first you have to download the openssl-0.9.8k_X64 or openssl-0.9.8k_WIN32 bits.
Google Archive link: Download Openssl For Windows From Google Archive
Then, you have to extract the downloaded zip folder in a desired location of your local disk, copy the path to the openssl’s bin folder, for example C:\openssl-0.9.8k_X64\bin\openssl

Then the system will ask you to enter a password for this key hash,
you can enter any password you want, the mostly used password is android.

After the password has been set, the key hash will be generated.

There are also other ways to do this, so explore~