Chris
Chris

Reputation: 2034

How to get location of a widget in order to position another widget when it loads

I've found several methods explaining how to get the location of a widget when pressing a button etc but whenever I try to get the location of a widget (during initState, didChangeDependencies, or after the main widget build method), in order to position another widget relative to the first ,I get findRenderObject was called on null ...

import 'package:flutter/material.dart';

class Example extends StatefulWidget {
  @override
  _ExampleState createState() => _ExampleState();
}

class _ExampleState extends State<Example> {
  GlobalKey _key = GlobalKey();
  RenderBox renderBox;

  void _getWidgetPosition(){
     renderBox = _key.currentContext.findRenderObject();
    final position = renderBox.localToGlobal(Offset.zero);
    print(position);
  }

  @override
  void initState(){
    super.initState();
// Getting the position doesn't work here
  }

  @override
  void didChangeDependencies(){
    super.didChangeDependencies();
// Getting the position doesn't work here
  }

  @override
  Widget build(BuildContext context) {
// Getting the position doesn't work here
    return Scaffold(
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: <Widget>[
          FloatingActionButton(onPressed: () => _getWidgetPosition()),
          FloatingActionButton(
            onPressed: null,
            key: _key,
          ),
          FloatingActionButton(onPressed: null)
        ],
      ),
      body: Stack(
        children: <Widget>[
          Positioned(
            // This is where I would like to use the middle FAB's position
            // left: ,
            // top: ,
            child: Container()),
        ],
      ),
    );
  }
}

Upvotes: 1

Views: 2739

Answers (1)

chunhunghan
chunhunghan

Reputation: 54367

You can copy paste run full code below
You can use addPostFrameCallback in initState
code snippet

class _ExampleState extends State<Example> {
  GlobalKey _key = GlobalKey();
  RenderBox renderBox;
  Offset position = Offset.zero;

  void _getWidgetPosition() {
    renderBox = _key.currentContext.findRenderObject();
    position = renderBox.localToGlobal(Offset.zero);
    print(position);
    setState(() {});
  }

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _getWidgetPosition();
    });

working demo

enter image description here

full code

import 'package:flutter/material.dart';

class Example extends StatefulWidget {
  @override
  _ExampleState createState() => _ExampleState();
}

class _ExampleState extends State<Example> {
  GlobalKey _key = GlobalKey();
  RenderBox renderBox;
  Offset position = Offset.zero;

  void _getWidgetPosition() {
    renderBox = _key.currentContext.findRenderObject();
    position = renderBox.localToGlobal(Offset.zero);
    print(position);
    setState(() {});
  }

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _getWidgetPosition();
    });
// Getting the position doesn't work here
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
// Getting the position doesn't work here
  }

  @override
  Widget build(BuildContext context) {
    print(position.dx);
// Getting the position doesn't work here
    return Scaffold(
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: <Widget>[
          FloatingActionButton(onPressed: () => _getWidgetPosition()),
          FloatingActionButton(
            onPressed: null,
            key: _key,
          ),
          FloatingActionButton(onPressed: null)
        ],
      ),
      body: Stack(
        children: <Widget>[
          Positioned(
              left: position.dx,
              top: position.dy,
              child: Container(
                color: Colors.orange,
                width: 100,
                height: 100,
              )),
        ],
      ),
    );
  }
}

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

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

Upvotes: 2

Related Questions