how to obtain RenderObject of lowest level widget?(RenderObject for SelectableText) - flutter

I want to obtain RenderObject of SelectableText because I want obtain RenderBox of the text, which, to my understanding, is obtainable via RenderObject.
So, I try to use GlobalKey to access the RenderObject by using this GlobalKey().currentContext?.findRenderObject()
But what I got is not what I want...
This is the demo program:
class Demo extends StatefulWidget {
const Demo({Key? key}) : super(key: key);
#override
State<Demo> createState() => _DemoState();
}
class _DemoState extends State<Demo> {
final _selectableTextKey = GlobalKey();
#override
Widget build(BuildContext context) {
return SelectableText(
'This is a test',
key: _selectableTextKey,
onTap: () {
print(
'render object == ${_selectableTextKey.currentContext?.findRenderObject()}');
},
);
}
}
This is the output in the console:
flutter: render object == RenderSemanticsAnnotations#52070 relayoutBoundary=up4
As illustrated in the screenshot of the DetailTree
I want the RenderObject of green arrow's, but I got red arrow's instead.
Using GlobalKey to obtain RenderObject then to access the RenderBox is all I know so far, so I don't have other idea to try...

Related

Screen's build method triggered when I use the navigator with rootNavigator

There is an interesting problem. I'm using Flutter 2.0 in my app. It has a bottom navigation bar. When I push to the new screen using "Navigator.of(context, rootNavigator: true)" along with the new screen, the screen that provides navigation is also rebuilding.
CategoriesView build() method
#override
Widget build(BuildContext context) {
print("Building: CategoryView");
return mobileBody;
}
ProductDetailView build() method
#override
Widget build(BuildContext context) {
print("Building: ProductDetailView");
return mobileBody;
}
When i use the "context.rootNavigator.pushNamed(ProductDetailView.route);" in CategoryView output is:
I/flutter (21910): Building: ProductDetailView
I/flutter (21910): Building: CategoryView
Without rootNavigator output is (context.navigator.pushNamed(ProductDetailView.route);):
I/flutter (21910): Building: ProductDetailView
Navigator extensions:
extension NavigationExtension on BuildContext {
NavigatorState get rootNavigator => Navigator.of(this, rootNavigator: true);
NavigatorState get navigator => Navigator.of(this);
}
Why is this happening? How can I prevent this?
I have met the same issue too, currently use a StatefulWidget wrapper for it.
class WrapperStateless extends StatefulWidget {
const WrapperStateless({Key key}) : super(key: key);
#override
_WrapperStatelessState createState() => _WrapperStatelessState();
}
class _WrapperStatelessState extends State<WrapperStateless> {
TabA tabA;
#override
void initState() {
super.initState();
tabA = TabA();
}
#override
Widget build(BuildContext context) {
// return tabA;
return tabA;
}
#override
void didUpdateWidget(WrapperStateless oldWidget) {
super.didUpdateWidget(oldWidget);
// setState(() {});
}
}

Flutter: How can I pass values in the constructor so I can reuse my widgets?

I am quite puzzled about the values being passed in the class constructor not being available in the Widget.
I am passing the value of the cards in the widget constructor, but when debugging it and after they are build the Text widgets do not have any text.
Initializing the Widget with the values.
Debugger shows the cardValue fields with no value.
Empty Widget:
This should work:
class PockerCard extends StatefulWidget {
final String cardValue;
const PockerCard({Key key, this.cardValue}) : super(key: key);
#override
_PockerCardState createState() => _PockerCardState();
}
class _PockerCardState extends State<PockerCard> {
#override
Widget build(BuildContext context) {
return Container(
child: Text(widget.cardValue),
);
}
}

Double nested UI layout causing StackOverflow on null call()

My app uses a hierachy of UI designs and extended designs for the correct layout (naturally).
One of my hierachies is a simple 3 layer approach:
HomePage (content) ->
UiBaseMain (structure for content) ->
UiBase (scaffolding)
Details: (and an MVCE) - I have tried narrowing down to the simplest form to get repeatedly throw this error.
Tested on both physical devices and AVD, both give a Stack Overflow error.
UiBase (ui_component_base.dart) (which all layouts are based on)
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class UiBase extends StatelessWidget {
final Widget widget;
const UiBase({Key key, this.widget})
: super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: const EdgeInsets.only(left: 8.0, right: 8.0),
width: double.infinity,
child: widget),
);
}
}
UiBaseMain (ui_main_navbar_appbar.dart) (where all Main UI content is shown with i.e. Dashboard, settings, etc which ideally alsop contains a small logo ontop, a BottomNavigationBar, etc)
import 'package:mytestapp/ui/design/ui_component_base.dart';
import 'package:flutter/widgets.dart';
class UiBaseMain extends StatefulWidget {
final Widget widget;
const UiBaseMain({Key key, this.widget}) : super(key: key);
#override
_UiBaseMain createState() => _UiBaseMain();
}
class _UiBaseMain extends State<UiBaseMain> {
#override
Widget build(BuildContext context) {
return UiBase(
widget: widget,
);
}
}
Finally, HomePage (ui_homepage.dart) (which is a content page, should show user content, etc)
import 'package:mytestapp/ui/design/ui_main_navbar_appbar.dart';
import 'package:flutter/widgets.dart';
class HomePage extends StatefulWidget {
const HomePage({Key key}) : super(key: key);
#override
_HomePage createState() => _HomePage();
}
class _HomePage extends State<HomePage> {
#override
Widget build(BuildContext context) {
return UiBaseMain(
widget: Text("test"),
);
}
}
Problem
Using the above structure, I consistently get the following Stack Overflow error
======== Exception caught by widgets library =======================================================
The following StackOverflowError was thrown building Material(type: canvas, color: Color(0xfffafafa), dependencies: [_InheritedTheme, _LocalizationsScope-[GlobalKey#1c4c4], _EffectiveTickerMode], state: _MaterialState#b2ccd):
Stack Overflow
The relevant error-causing widget was:
Scaffold file:///C:/Users/CybeX/mytestapp/mytestapp-mobile-flutter/lib/ui/design/ui_component_base.dart:14:12
When the exception was thrown, this was the stack:
#0 _WordWrapParseMode.values (package:flutter/src/foundation/diagnostics.dart:776:6)
#1 _SyncIterator.moveNext (dart:core-patch/core_patch.dart:165:25)
#2 Iterable.length (dart:core/iterable.dart:429:15)
#3 _PrefixedStringBuilder._finalizeLine (package:flutter/src/foundation/diagnostics.dart:856:30)
#4 _PrefixedStringBuilder.write (package:flutter/src/foundation/diagnostics.dart:973:9)
...
====================================================================================================
======== Exception caught by widgets library =======================================================
The method 'call' was called on null.
Receiver: null
Tried calling: call()
The relevant error-causing widget was:
Scaffold file:///C:/Users/CybeX/mytestapp/mytestapp-mobile-flutter/lib/ui/design/ui_component_base.dart:14:12
====================================================================================================
======== Exception caught by widgets library =======================================================
The method 'call' was called on null.
Receiver: null
Tried calling: call()
The relevant error-causing widget was:
Scaffold file:///C:/Users/CybeX/mytestapp/mytestapp-mobile-flutter/lib/ui/design/ui_component_base.dart:14:12
====================================================================================================
Question
Is this an issue my side or is this an issue others can confirm and infact a bug in the Flutter framework?
You have to pass here widget.widget because it is a stateful widget, no? : return UiBase(
widget: widget,
);
Sorry not convenient to write from phone.

array of items as state in a StatefulWidget & ensuring "setState" only triggers updates for items in the array that has changed?

Background - want to utilise a dynamic list of items for a StatefulWidget. In my usecase the widget will be calling a CustomePainter (canvas) so sometimes there will be a varying number of images to be drawn on the canvas, hence within parent StatefulWidget would like to have an "array of images".
Question - if using an array as the state variable what do I need to do programmtically (if anything) to ensure only the items that have changed within the array do infact get "redrawn", in particular in this case get "re-painted" on the canvas.
(Perhaps there are two separate answers here one for the array having (a) standard widgets, and one for the case where (b) items are being passed to a CustomePainter for painting on a canvas??)
As an example see code below (#ch271828n provided this to assist me here on a separate question - Getting 'Future<Image>' can't be assigned to a variable of type 'Image' in this Flutter/Dart code?). This code highlights the main idea, but doesn't include the passing onto a CustomPainter as parameters.
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<ui.Image> _backgroundImages;
#override
void initState() {
super.initState();
_asyncInit();
}
Future<void> _asyncInit() async {
final imageNames = ['firstimage', 'secondimage', 'thirdimage'];
// NOTE by doing this, your images are loaded **simutanously** instead of **waiting for one to finish before starting the next**
final futures = [for (final name in imageNames) loadImage(name)];
final images = await Future.wait(futures);
setState(() {
_backgroundImages = images;
});
}
Future<ui.Image> loadImage(imageString) async {
ByteData bd = await rootBundle.load(imageString);
// ByteData bd = await rootBundle.load("graphics/bar-1920×1080.jpg");
final Uint8List bytes = Uint8List.view(bd.buffer);
final ui.Codec codec = await ui.instantiateImageCodec(bytes);
final ui.Image image = (await codec.getNextFrame()).image;
return image;
// setState(() => imageStateVarible = image);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: _backgroundImages != null ? YourWidget(_backgroundImages) : Text('you are loading that image'),
),
);
}
}
Firstly, talking about builds: Indeed you need a state management solution. Maybe look at https://flutter.dev/docs/development/data-and-backend/state-mgmt/options. Personally I suggest MobX which requires few boilerplate and can make development much faster.
Using setState is not a good idea. The setState, when looking into source code, does nothing but:
void setState(VoidCallback fn) {
assert(...);
_element.markNeedsBuild();
}
So it is nothing but markNeedsBuild - the whole stateful widget is called build again. Your callback passed into setState has nothing special.
Thus this way cannot trigger partial rebuild.
Secondly, you only want to reduce repaint, but not reduce number of builds, because flutter is designed such that build can be called by 60fps easily. Then things become easy:
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Stack(
children: [
for (final image in _yourImages)
CustomPaint(
painter: _MyPainter(image),
),
],
);
}
}
class _MyPainter extends CustomPainter {
final ui.Image image;
_MyPainter(this.image);
#override
void paint(ui.Canvas canvas, ui.Size size) {
// paint it
}
#override
bool shouldRepaint(covariant _MyPainter oldDelegate) {
return oldDelegate.image != this.image;
}
}
Notice that, you can call setState in homepage whenever you like, because that is cheap. (if your homepage has a lot of children widget then maybe not good, then you need state management solution). But the painter will not repaint unless shouldRepaint says so. Yeah!

'call' was called on null for parent callback function from stateful widget

I want to have a multi-section form which has several subforms which when completed and validated will callback to the parent widget in order to go to the next subform.
my issue is I keep getting an error saying the function is null and I'm not sure why.
iv tried having a function within the StatefulWidget of the child but I always get an error saying the function is null when calling the parents passed through 'complete' function.
I/flutter (23821): ══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
I/flutter (23821): The following NoSuchMethodError was thrown while handling a gesture:
I/flutter (23821): The method 'call' was called on null.
I/flutter (23821): Receiver: null
I/flutter (23821): Tried calling: call()
I thought I was doing it correctly but am a bit lost on what to try next, here is my code :
parent widget
class CreateForm extends StatefulWidget {
CreateForm({
Key key
}): super(key: key);
CreateFormState createState() => CreateFormState();
}
class CreateFormState extends State < CreateForm > {
bool userDetailsCompleted = false;
final formKey = GlobalKey < FormState > ();
#override
Widget build(BuildContext context) {
return !userDetailsCompleted ? UserDetailsForm(
completed: () {
print("testing");
// setState(() {
// userDetailsCompleted = true;
// });
},
) : nextSection(
);
}
}
child widget
class UserDetailsForm extends StatefulWidget {
final Function completed;
UserDetailsForm({
#required this.completed,
Key key
}): super(key: key);
UserDetailsFormState createState() => UserDetailsFormState();
}
class UserDetailsFormState extends State < UserDetailsForm > {
#override
Widget build(BuildContext context) {
return Form(
child: CustomButton(
size: 15,
text: 'Submit',
onPressed: () {
print('UserDetails account form submit');
widget.completed();
},
)
);
}
}
I tried your code locally, and it works. I think you just need to restart the Flutter, as maybe you just replaced Stateles to Stateful widget a moment before
Fully-working repo
You can build it locally also in this Github repo
demo