Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

是否有更好的初始化intro的时机? Null check operator used on a null value #31

Closed
lizhuoyuan opened this issue Jun 23, 2021 · 7 comments
Assignees
Labels
bug Something isn't working

Comments

@lizhuoyuan
Copy link

lizhuoyuan commented Jun 23, 2021

目前看example中是在state的构造函数中初始化的。

intro肯定是要在build函数之前 , 因为build函数中会调用intro.keys[] 来设置key , 而因为intro中要设置UI,很有可能使用context,而过早初始化又获取不到context。

还有一个问题是,我在调用intro.start(context)的时候 , 确认了context和intro都不为空, 但是报错:Null check operator used on a null value(通过catch intro.start(context) 获取到的信息)

@lizhuoyuan lizhuoyuan changed the title 是否有更好的初始化intro的方法? 是否有更好的初始化intro的时机? Jun 23, 2021
@lizhuoyuan lizhuoyuan changed the title 是否有更好的初始化intro的时机? 是否有更好的初始化intro的时机? Null check operator used on a null value Jun 23, 2021
@hellohejinyu hellohejinyu self-assigned this Jun 23, 2021
@hellohejinyu
Copy link
Member

hellohejinyu commented Jun 23, 2021

Null check operator used on a null value 这个问题我看到 issue 列表里面有个人也说了。最近工作有点忙,没时间看,不知道方不方便提供一个最小复现 demo。

@hellohejinyu hellohejinyu added the bug Something isn't working label Jun 23, 2021
@hellohejinyu
Copy link
Member

时机问题我再考虑考虑,不知道你有没有一些好的想法。

@lizhuoyuan
Copy link
Author

lizhuoyuan commented Jun 23, 2021

时机问题我再考虑考虑,不知道你有没有一些好的想法。

暂时也没有太仔细考虑 .
不过还是说一下我目前的做法.

给组件加key的时候 , 不直接使用intro.keys , 而是自己定义一个key .
在实例化intro之后,再修改intro.keys.

关于context , 我是用了一个全局的key, 通过它来获取的context , 不过有点问题, 比如某些地方对context有要求(好像这个start的context参数就不能用全局的context).

时机上 , 我是在initState中 , 通过Future.delay来实例化intro, 但是时间如果是0秒是没法立即弹出浮层的 , 当时猜测是实例化有异步操作导致方法执行完但并没有实例化完成,并且方法没有标注成异步方法 , 这个也是因为没有去看源码所以不确定.

最后表示对不能及时维护开源项目表示理解 , 以及感谢.

下面这个是弹出后点击阴影部分出现的null check问题, 和我之前遇到的不一样,具体回头有空我再尽量提供一个demo.
start出现null check的时候 主要是我想第一时间弹出引导(是确认了context和intro的key不为空)的时候catch到的,此时没有弹出引导。

import 'package:flutter/material.dart';
import 'package:flutter_intro/flutter_intro.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with AutomaticKeepAliveClientMixin {
  late Intro intro;

  GlobalKey firstKey = GlobalKey();

  void initIntro() {
    intro = Intro(
      noAnimation: false,
      stepCount: 2,
      maskClosable: true,
      onHighlightWidgetTap: (introStatus) {
        print(introStatus);
      },
      maskColor: Color(0xcc1c1c1e),
      padding: EdgeInsets.all(5),
      borderRadius: BorderRadius.circular(16),
      widgetBuilder: (StepWidgetParams stepWidgetParams) {
        print(stepWidgetParams);
        return Text('ad');
      },
    );
    intro.keys[0] = firstKey;
  }

  @override
  void reassemble() {
    super.reassemble();
    intro.start(context);
  }

  @override
  void initState() {
    super.initState();

    initIntro();
    Future.delayed(Duration(milliseconds: 0), () {
      showGuide();
    });
  }

  void showGuide() {
    try {
      intro.start(context);
    } catch (e) {
      print(e);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Container(
        key: firstKey,
        child: Text(
          'You have pushed the button this many times:',
        ),
      ),
    );
  }

  @override
  // TODO: implement wantKeepAlive
  bool get wantKeepAlive => true;
}

//Performing hot reload...
// Syncing files to device V1981A...
// I/flutter (24304): StepWidgetParams(currentStepIndex: 0, stepCount: 2, size: Size(307.0, 26.0), screenSize: Size(360.0, 802.7), offset: Offset(-5.0, 87.0))
// Reloaded 1 of 554 libraries in 346ms.
//
// ======== Exception caught by gesture ===============================================================
// The following _CastError was thrown while handling a gesture:
// Null check operator used on a null value
//
// When the exception was thrown, this was the stack:
// #0      Intro._getWidgetInfo (package:flutter_intro/flutter_intro.dart:148:33)
// #1      Intro._createStepWidget (package:flutter_intro/flutter_intro.dart:307:5)
// #2      Intro._renderStep (package:flutter_intro/flutter_intro.dart:332:5)
// #3      Intro._onNext (package:flutter_intro/flutter_intro.dart:284:7)
// #4      Intro._showOverlay.<anonymous closure>.<anonymous closure> (package:flutter_intro/flutter_intro.dart:241:35)
// ...
// Handler: "onTap"
// Recognizer: TapGestureRecognizer#1feed
//   debugOwner: GestureDetector
//   state: possible
//   won arena
//   finalPosition: Offset(282.7, 198.7)
//   button: 1
//   sent tap down
// ====================================================================================================

@hellohejinyu
Copy link
Member

时机问题我再考虑考虑,不知道你有没有一些好的想法。

暂时也没有太仔细考虑 .
不过还是说一下我目前的做法.

给组件加key的时候 , 不直接使用intro.keys , 而是自己定义一个key .
在实例化intro之后,再修改intro.keys.

关于context , 我是用了一个全局的key, 通过它来获取的context , 不过有点问题, 比如某些地方对context有要求(好像这个start的context参数就不能用全局的context).

时机上 , 我是在initState中 , 通过Future.delay来实例化intro, 但是时间如果是0秒是没法立即弹出浮层的 , 当时猜测是实例化有异步操作导致方法执行完但并没有实例化完成,并且方法没有标注成异步方法 , 这个也是因为没有去看源码所以不确定.

最后表示对不能及时维护开源项目表示理解 , 以及感谢.

下面这个是弹出后点击阴影部分出现的null check问题, 和我之前遇到的不一样,具体回头有空我再尽量提供一个demo.
start出现null check的时候 主要是我想第一时间弹出引导(是确认了context和intro的key不为空)的时候catch到的,此时没有弹出引导。

import 'package:flutter/material.dart';
import 'package:flutter_intro/flutter_intro.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with AutomaticKeepAliveClientMixin {
  late Intro intro;

  GlobalKey firstKey = GlobalKey();

  void initIntro() {
    intro = Intro(
      noAnimation: false,
      stepCount: 2,
      maskClosable: true,
      onHighlightWidgetTap: (introStatus) {
        print(introStatus);
      },
      maskColor: Color(0xcc1c1c1e),
      padding: EdgeInsets.all(5),
      borderRadius: BorderRadius.circular(16),
      widgetBuilder: (StepWidgetParams stepWidgetParams) {
        print(stepWidgetParams);
        return Text('ad');
      },
    );
    intro.keys[0] = firstKey;
  }

  @override
  void reassemble() {
    super.reassemble();
    intro.start(context);
  }

  @override
  void initState() {
    super.initState();

    initIntro();
    Future.delayed(Duration(milliseconds: 0), () {
      showGuide();
    });
  }

  void showGuide() {
    try {
      intro.start(context);
    } catch (e) {
      print(e);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Container(
        key: firstKey,
        child: Text(
          'You have pushed the button this many times:',
        ),
      ),
    );
  }

  @override
  // TODO: implement wantKeepAlive
  bool get wantKeepAlive => true;
}

//Performing hot reload...
// Syncing files to device V1981A...
// I/flutter (24304): StepWidgetParams(currentStepIndex: 0, stepCount: 2, size: Size(307.0, 26.0), screenSize: Size(360.0, 802.7), offset: Offset(-5.0, 87.0))
// Reloaded 1 of 554 libraries in 346ms.
//
// ======== Exception caught by gesture ===============================================================
// The following _CastError was thrown while handling a gesture:
// Null check operator used on a null value
//
// When the exception was thrown, this was the stack:
// #0      Intro._getWidgetInfo (package:flutter_intro/flutter_intro.dart:148:33)
// #1      Intro._createStepWidget (package:flutter_intro/flutter_intro.dart:307:5)
// #2      Intro._renderStep (package:flutter_intro/flutter_intro.dart:332:5)
// #3      Intro._onNext (package:flutter_intro/flutter_intro.dart:284:7)
// #4      Intro._showOverlay.<anonymous closure>.<anonymous closure> (package:flutter_intro/flutter_intro.dart:241:35)
// ...
// Handler: "onTap"
// Recognizer: TapGestureRecognizer#1feed
//   debugOwner: GestureDetector
//   state: possible
//   won arena
//   finalPosition: Offset(282.7, 198.7)
//   button: 1
//   sent tap down
// ====================================================================================================

你上面 ,stepCount: 2设置了两步,但是你只用了一个 key。

@lizhuoyuan
Copy link
Author

那这里的问题就了解了, 但之前遇到的不是这样的,而且没有报错, 只能try catch才能看到那个信息。
回头我看能不能写个示例来复现吧

@hellohejinyu
Copy link
Member

那这里的问题就了解了, 但之前遇到的不是这样的,而且没有报错, 只能try catch才能看到那个信息。
回头我看能不能写个示例来复现吧

好的,感谢🙏

@hellohejinyu
Copy link
Member

我重写了 3.0 版本,现在通过 InheritedWidget 来实现的,也不要求在一开始固定声明引导页的数量。初始化我现在建议的方案是在第一个引导组件的 onWidgetLoad 回调函数里面执行 Intro.of(context)?.start()onWidgetLoad 可以确保这个组件在上下文 context 中了。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants