This article, the 17th in a series of articles, once again introduces practical techniques for the development of Flutter to help you continue to overtake cars on curves. The whole article is a personal summary of the practical things that Flutter can do every day, with a focus on filling holes in the road so that you don’t have to go on the wrong side of the road.

Article summary address:

A complete series of articles on Flutter

A series of articles on the world outside Flutter

1, Package get git failed

The Flutter project usually references a third library directly from a pub plugin, but sometimes we choose to use git references for security and privacy. For example:

  photo_view:
    git:
      url: https://github.com/CarSmallGuo/photo_view.git
      ref: master
Copy the code

At this time, if the flutter packages get fails, the following problems may occur when the flutter Packages GET is executed again:

Git fails because of flutter packages get:

The next time the package is pulled, the git directory in.pub_cache will detect an existing directory, but it may be empty, etc., causing an exception when the flutter packages get occurs.

So you need to clear it out.pub_cacheWithin thegitException directory, and then it is best to clear the project underpubspec.lock, and then execute againflutter packages get

Git folder in C: Users\ XXXXX \AppData\ Pub\Cache folder

The MAC directory is ~/. Pub-cache.

2, TextEditingController

As shown in the code above, the red line indicates that if the controller is empty, a TextEditingController is assigned. Writing this way causes the problem shown below:

After successful input when the keyboard is popped up, the input disappears when the keyboard is folded up! This is because keyboard pop-ups and takedowns trigger page builds, and each TextEditingController assignment causes the TextEditingValue of the TextField to reset when the Controller is null.

As shown in the figure above, the TextEditingController needs to be built globally before assigning, since the value is not copied during update when the Controller of the TextField is not empty. If the controller is empty, null is used instead of reconstructing the TextEditingController every time you build.

3, Scrollable

As shown in the figure above, as analyzed in article 7, Scrollable usually exists in the slider list, and Scrollable happens to be an InheritedWidget, This makes it convenient to call Scrollable related methods in children.

We can control the children of the ListView/GridView more decoupled by scrollable.of (context) as shown in the following code.

ScrollableState state = scrollable. of(context) // get renderObject of viewport in _scrollable state.context.findRenderObject(); / / / to monitor position update state. The position. AddListener (() {}); / / / notify position update state. The position. NotifyListeners (); /// Scroll to the specified position state.position.jumpto (1000); ...Copy the code

4. Gaussian blur

With Flutter, BackdropFilter and ImageFilter support gaussian blur, as shown in the code below, can quickly achieve the gaussian blur effect shown above.

class BlurDemoPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: new Container(
        child: Stack(
          children: <Widget>[
            Positioned(
              top: 0,
              bottom: 0,
              left: 0,
              right: 0,
              child: new Image.asset(
                "static/gsy_cat.png",
                fit: BoxFit.cover,
                width: MediaQuery.of(context).size.width,
                height: MediaQuery.of(context).size.height,
              )),
            new Center(
              child: new Container(
                width: 200,
                height: 200,
                child: ClipRRect(
                  borderRadius: BorderRadius.circular(15.0),
                  child: BackdropFilter(
                    filter: ImageFilter.blur(sigmaX: 8.0, sigmaY: 8.0),
                    child: new Row(
                      mainAxisSize: MainAxisSize.max,
                      crossAxisAlignment: CrossAxisAlignment.center,
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: <Widget>[
                        new Icon(Icons.ac_unit),
                        new Text("Wow!!)],)))))],)); }}Copy the code

5. Scroll to the specified position

Because Flutter currently does not provide a direct way to scroll to a given Item, a compromise can be achieved quickly with the code shown below if each Item is of different sizes:

Dart scroll_to_index_DEMO_page2.dart Add a GlobalKey to each item and find the RenderBox for each item. Then use localToGlobal to get the offset of the item in the ViewPort ancestor and scroll:

Of course, there is another way to do this, scroll_to_index_DEMO_page.dart

6, findRenderObject

There is a difference between container widgets and render widgets in Flutter.

  • Text,SliverListTileAnd so on all belong to render Widget, its interior is mainlyRenderObjectElement
  • StatelessWidget / StatefulWidgetEtc are container widgets that are used internally byComponentElementComponentElementIt doesn’t existRenderObject.

BuildContext is Element, so context.findRenderObject() is Element’s findRenderObject().

Then, as shown in the figure above, the implementation of findRenderObject is to obtain the renderObject. In Element, the obtaining logic of renderObject is very clear. When meeting ComponentElement, Element. VisitChildren (visit); , recurse until RenderObjectElement is found.

So as shown in the following code print (” ${globalKey. CurrentContext. FindRenderObject ()} “); The SizedBox RenderObject is output.

7. Line spacing

There is no direct method for setting the spacing between Text lines in Flutter. The Text effect should look like this:

So how do we deal with line spacing? As shown in the figure below, by setting StrutStyle leading, Transform is used to calculate the orientation and position offset. Since leading is balanced up and down, the required line spacing can be obtained after calculation. (Although there is no guarantee of 100% pixel accuracy, do you know of any other methods?)

As an additional point, it can be used by the parent nodeDefaultTextStyleTo achieve local style sharing oh.

8 Builder.

There is a Builder Widget in Flutter. This Widget is a simple encapsulation of the StatelessWidget. Why should it exist?

As shown in the following figure, some Flutter developers may experience the following error when using scaffold.of (context).showsnackbar (snackbar). This is because the context passed in is the wrong node. Because the context passed in here did not find the Scaffold where the page was.

The Scaffold Builder method returns the given context. When you look up the Scaffold, you can find the Scaffold of the parent node. That’s part of what ComponentElement does.

9, fast implementation of animation switching effect

To achieve the animation effects shown above, an AnimatedSwitcher encapsulation is provided in Flutter for easy implementation.

As shown in the figure below, you can achieve the effect of animation switch by nesting AnimatedSwitcher, specifying the Animation Effect transitionBuilder, and then changing the key value of the animation to be executed when the data changes.

10, multi-language display exception

At the official github.com/flutter/flu… It can be found in the issue that Flutter is displayed in Korean/Japanese and Chinese at the same time, which will cause abnormal text rendering in iOS, as shown in the picture below. The abnormal situation is on the left.

There are two solutions to the problem:

  • Added font TTF to specify globally changing font display.

  • Modify the theme under allTextThemefontFamilyFallback

getThemeData() {
  var themeData = ThemeData(
        primarySwatch: primarySwatch
   );

    var result = themeData.copyWith(
      textTheme: confirmTextTheme(themeData.textTheme),
      accentTextTheme: confirmTextTheme(themeData.accentTextTheme),
      primaryTextTheme: confirmTextTheme(themeData.primaryTextTheme),
    );
    returnresult; ConfirmTextTheme (TextTheme TextTheme) {getCopyTextStyle(TextStyle TextStyle) {return textStyle.copyWith(fontFamilyFallback: ["PingFang SC"."Heiti SC"]);
  }

  return textTheme.copyWith(
    display4: getCopyTextStyle(textTheme.display4),
    display3: getCopyTextStyle(textTheme.display3),
    display2: getCopyTextStyle(textTheme.display2),
    display1: getCopyTextStyle(textTheme.display1),
    headline: getCopyTextStyle(textTheme.headline),
    title: getCopyTextStyle(textTheme.title),
    subhead: getCopyTextStyle(textTheme.subhead),
    body2: getCopyTextStyle(textTheme.body2),
    body1: getCopyTextStyle(textTheme.body1),
    caption: getCopyTextStyle(textTheme.caption),
    button: getCopyTextStyle(textTheme.button),
    subtitle: getCopyTextStyle(textTheme.subtitle),
    overline: getCopyTextStyle(textTheme.overline),
  );
}
Copy the code

Ps: by WidgetsBinding. The instance. The window. The locale; You can get the current language of the mobile platform itself, not the context, not the Locale that you’ve set.

11. Long pressing the input box leads to abnormal situations

If the project has multi-language and theme switching scenarios, exceptions may occur due to long pressing of the input box. At present, two methods are recommended to solve the problem:

  • 1, can give you customThemeDataForce a fixed platform to be specified, but this method will cause the platform copy-paste pop-up box without platform features:
Platform: targetplatform. androidCopy the code
  • 2, add a customLocalizationsDelegateTo achieve custom support in multi-language environments:

class FallbackCupertinoLocalisationsDelegate
    extends LocalizationsDelegate<CupertinoLocalizations> {
  const FallbackCupertinoLocalisationsDelegate();

  @override
  bool isSupported(Locale locale) => true;

  @override
  Future<CupertinoLocalizations> load(Locale locale) => loadCupertinoLocalizations(locale);

  @override
  bool shouldReload(FallbackCupertinoLocalisationsDelegate old) => false;
}

class CustomZhCupertinoLocalizations extends DefaultCupertinoLocalizations {
  const CustomZhCupertinoLocalizations();

  @override
  String datePickerMinuteSemanticsLabel(int minute) {
    if (minute == 1) return 'a minute';
    return minute.toString() + 'minutes';
  }

  @override
  String get anteMeridiemAbbreviation => 'morning';

  @override
  String get postMeridiemAbbreviation => 'afternoon';

  @override
  String get alertDialogLabel => 'warning';

  @override
  String timerPickerHourLabel(int hour) => 'hour';

  @override
  String timerPickerMinuteLabel(int minute) => 'points';

  @override
  String timerPickerSecond(int second) => '秒';

  @override
  String get cutButtonLabel => 'cut';

  @override
  String get copyButtonLabel => 'copy';

  @override
  String get pasteButtonLabel => 'paste';

  @override
  String get selectAllButtonLabel => 'all';
}

class CustomTCCupertinoLocalizations extends DefaultCupertinoLocalizations {
  const CustomTCCupertinoLocalizations();

  @override
  String datePickerMinuteSemanticsLabel(int minute) {
    if (minute == 1) return 'a minute';
    return minute.toString() + 'minutes';
  }

  @override
  String get anteMeridiemAbbreviation => 'morning';

  @override
  String get postMeridiemAbbreviation => 'afternoon';

  @override
  String get alertDialogLabel => 'warning';

  @override
  String timerPickerHourLabel(int hour) => 'hour';

  @override
  String timerPickerMinuteLabel(int minute) => 'points';

  @override
  String timerPickerSecond(int second) => '秒';

  @override
  String get cutButtonLabel => 'cut';

  @override
  String get copyButtonLabel => 'copy';

  @override
  String get pasteButtonLabel => 'paste';

  @override
  String get selectAllButtonLabel => 'all';
}

Future<CupertinoLocalizations> loadCupertinoLocalizations(Locale locale) {
  CupertinoLocalizations localizations;
  if (locale.languageCode == "zh") {
    switch (locale.countryCode) {
      case 'HK':
      case 'TW':
        localizations = CustomTCCupertinoLocalizations();
        break; default: localizations = CustomZhCupertinoLocalizations(); }}else {
    localizations = DefaultCupertinoLocalizations();
  }
  return SynchronousFuture<CupertinoLocalizations>(localizations);
}

Copy the code

Thus, canto xVII is finally over! (/ / / del / / /)

Resources to recommend

  • Making: github.com/CarGuo
  • Open Source Flutter complete project:Github.com/CarGuo/GSYG…
  • Open Source Flutter Multi-case learning project:Github.com/CarGuo/GSYF…
  • Open Source Fluttre Combat Ebook Project:Github.com/CarGuo/GSYF…
  • Open Source React Native project: github.com/CarGuo/GSYG…