FutureBuilder The method 'findRenderObject' was called on null

2,269

It is possible to query the text position after it renders.

For example, you can move ListView to a separate widget. When postframe callback is called, the text will already exist so you'll get its position

class _BookScreenState extends State<BookScreen> {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: ...,
      builder: (ctx, snapshot) =>
      snapshot.connectionState == ConnectionState.waiting
          ? Center(child: CircularProgressIndicator())
          : BooksList(data: snapshot.data),
    );
  }
}

class BooksList extends StatefulWidget {
  final BooksListData data;

  BooksList({@required this.data});

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

class _BooksListState extends State<BooksList> {
  final GlobalKey _itemKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: [
        RichText(
          key: _itemKey,
          text: TextSpan(
            children: _getTextSpan(widget.data),
          ),
        ),
      ],
    );
  }

  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      findRichText();
    });
  }

  void findRichText() {
    var richText = _itemKey.currentContext.findRenderObject() as RenderParagraph;
    print(richText.localToGlobal(Offset.zero));
  }
}

However this approach complicates the code and doesn't seem reliable.

Alternatively, if you want scrolling to listview item, you can use scrollable_positioned_list package. It provides more declarative api:

final ItemScrollController itemScrollController = ItemScrollController();

ScrollablePositionedList.builder(
  itemCount: ...,
  itemBuilder: (context, index) => ...,
  itemScrollController: itemScrollController,
);

itemScrollController.jumpTo(
  index: 100,
  alignment: 0.5,
);
Share:
2,269
ynsmtkl
Author by

ynsmtkl

Updated on December 20, 2022

Comments

  • ynsmtkl
    ynsmtkl over 1 year

    I want to render the position of a RichText built by a FutureBuilder as the code below, I used the WidgetsBinding.instance.addPostFrameCallback in the initState() but I got an error: The method 'findRenderObject' was called on null., I tried this approach without FutureBuilder works fine, I do not know how to solve this with FutureBuilder

    class BookScreen extends StatefulWidget {
      int bookId;
      BookScreen(this.bookId);
    
      @override
      _BookScreenState createState() => _BookScreenState();
    }
    
    class _BookScreenState extends State<BookScreen> {
    
      final GlobalKey _itemKey = GlobalKey();
    
    void initState() {
        super.initState();
    
    WidgetsBinding.instance.addPostFrameCallback((_) {findRichText();});
    
      }
    @override
      Widget build(BuildContext context) {
    
    return FutureBuilder(
          future: Provider.of<Book>(context, listen: false)
              .getBookDetail(widget.bookId),
          builder: (ctx, snapshot) => snapshot.connectionState == ConnectionState.waiting
                  ? Center(
                      child: CircularProgressIndicator(),
                    )
                  : ListView(
                      children: <Widget>[
                        Padding(
                          padding: const EdgeInsets.all(10.0),
                          child: RichText(
                            key: _itemKey, // Here is the global key
                            text: TextSpan(
                              children: _getTextSpan(snapshot.data),
                            ),
                          ),
                       ),
                    ],
                 ),
              );
    
    void findRichText() {
        var richText = _itemKey.currentContext.findRenderObject() as RenderParagraph;
        print(richText.localToGlobal(Offset.zero));
    }
    
    • Pavel
      Pavel almost 4 years
      findRichText is called "after the frame". At the time RichText doesn't exist yet. CircularProgressIndicator is shown instead because data is still loading
    • Pavel
      Pavel almost 4 years
      What do you want to achieve using position of text?
    • ynsmtkl
      ynsmtkl almost 4 years
      I have menu and I want to scroll to that position when the menu clicked, please how to implement that to the RichText be before the frame?
  • ynsmtkl
    ynsmtkl almost 4 years
    Thank you, your answer is correct, but as you say I am also not happy with this approach so for that I have to change it. thanks