I'm trying to give a widget far down in the widget tree absolute positions. Because it is somewhere down in the widget tree, it is most likely that there will be certain layout constraints set by its ancestors. I'm looking for something that can ignore those constraints and just use absolute coordinates.
If the explanation is confusing, I drew a little scheme of something that represents my wanted outcome:
I tried doing this using a Stack, but that makes it difficult to pass in absolute coordinates, even with a Positioned because setting the property left: 0.0 would use relative coordinates to the ancestor. So setting it to zero would not necessarily mean that the widget would be positioned at the top left of the screen.
I also tried using an Overlay but I the results are pretty much the same as with the Stack implementation.
What is the recommended way of doing this in Flutter?
I'll describe two ways to do this, but I won't provide the code or an example as I don't think putting this out there in a copy-paste-able way is necessarily a good idea - I agree with #Remi's comment that if you need to do this in an app, there's a good chance that the app could be refactored to avoid it. However, there is a way to do this - although be warned, it might cause some problems with things such as touch events (although I think there's ways to get around that - see AbsorbPointer and IgnorePointer for starting points). Since you haven't really explained what you want this for, I'm going to assume that isn't something you need.
I would encourage you to look into other ways of doing whatever you're trying to do and ask if you'd like help figuring out that better way.
Anyways, on to the good stuff =P :
Method # 1 - using an existing overlay:
I have actually used this method myself for a modal loading indicator which I want to persist while between pages underneath are being pushed and which can be called from anywhere within an app without requiring any widgets be in the tree above it. That's a very specific use-case though which I think justifies this usage.
Basically, you want to use an Overlay, but instead of creating your own (which will have the same size as the widget you're dealing with), you want to use an existing one. If you're using a MaterialApp, WidgetApp, or even just a Navigator somewhere in your app, you have an Overlay already which is almost for sure the same size as the screen. If you have multiple layers of overlays, you want to get the one at the top as that's the most likely to cover the entire screen.
To get access to it, you can use OverlayState rootOverlay = context.rootAncestorStateOfType(const TypeMatcher<OverlayState>()); to get ahold of the root overlay (you'll probably want to assert that it actually found one).
Once you have access to it, you can make an OverlayEntry which builds whatever your widget is, and call rootOverlay.insert(...) to add it. The item built by your OverlayEntry will be positioned from the top left and to the extent of the screen so long as the overlay itself covers the entire screen (you can do the offset yourself). You'll also want to make sure you rootOverlay.remove(...) at some point, which means keeping a reference to the OverlayEntry. I'd personally create a class called OverlayInsertionManager or something which keeps track of the overlay entry and does insertion/removal.
Method #2 - using your own widget
I would consider this way of doing it slightly cleaner if you're just doing it within your own app, although still probably not a great idea.
Basically, what you want to do is create a Stateful Widget high up in your app - above anything that would take up space on the screen. This could theoretically mean above your MaterialApp/WidgetApp/etc although that might cause problems for you if you're using theme/text directionality/etc that WidgetApp provides. I think you can use the WidgetApp.builder to place your widget where you need it. Let's call it PageOverlay for convenience (with a corresponding PageOverlayState.
In your widget you'll have a static of method like the following (these are littered throughout flutter's source code and are more or less a convention):
static PageOverlayState of(BuildContext context) {
final PageOverlayState result = context.ancestorStateOfType(const TypeMatcher<PageOverlayState>());
assert(() {
if (result == null) {
throw new FlutterError(
'No Overlay widget found.\n'
);
}
return true;
}());
return result;
}
Within your PageOverlayState, you're going to have a variable something like WidgetBuilder _overlayBuidler which is normally null, and a method/setter something like set overlayBuilder(WidgetBuilder overlayBuilder) => setState(() => _overlayBuilder = overlayBuilder);
In the PageOverlayState's builder, you'll create a Stack; the first child will be the child you get passed into the WidgetApp.builder or MaterialApp.builder. You'll only create a second child if _overlayBuilder is not null; if it is not null the second child should be something like new Builder(builder: _overlayBuilder).
You might have to do something around sizing the stack properly (i.e. put it in an Expanded or something).
Related
I want to practice creating simple apps using no storyboard. I am able to do the constraints programmatically (slowly) but I want to also practice separating my code into MVC. Is there a particular place/method that I am supposed to write the programatic constraints? Or does it not matter?
Good discussion in the comments. My thoughts, based on that discussion?
With an understanding that the question is subjective, you place your constraints:
The earliest in a view controller's life cycle where they work.
As "close" to the view as possible.
If it's something common, make it as universal as possible.
Understand how your specifics fit into everything.
(Understand, the question isn't limited to constraints. It could apply to hierarchies, UI, even database tables when you get down to it!)
Sticking to constraints, and my answer....
(1) Use the UIViewController and UIView lifecycles.
Generally the view life cycle is loadView, viewDidLoad, viewWillAppear, viewWillLayoutSubviews, viewDidLayoutSubviews, and viewDidAppear. great SO answer detailing this.
I believe that loadView is too early for constraints, but not viewDidLoad - **provided you aren't expecting to know the frame size. While many say viewDidLayoutSubviews is the right place for that, I've found that viewWillLayoutSubviews most times works just as well. Either way, get your constraints set as soon as possible!
(2) Do it as close to the view as possible.
If you have subviews - I have a "ToolBar" class of objects - you want the constraints, at least as much as possible, to be coded inside the class. For instance, in my tool bar, it slides out, has buttons, and even rotates upon orientation. The only constraints not inside these classes is for orientation - that owner is (and I believe should be) the view controller instantiating it.
(3) Make it universal.
I plan to use this tool bar across a few apps. So the first thing I did was add it to a framework. This framework was needed because I had an app that I delivered a photo editing exension - and the "edit" screen is as much the same as possible. In the end I move all my constraints there. (At least as much as possible.) Anything that I believe is reusable.
(4) Understand the specific requirements of your app.
This should be obvious. If you need to code for various orientations, use arrays and activate/deactivate them. (YES, a common mistake is replacing them! That's setting yourself up for some major headaches.)
If you can keep things active, declare the constraint, set `isActive = true1, and forget about it. If you need to adjust that constraint's constant or multiplier, in the declaration name it and then where you need to alter it, do it.
My conclusion? Auto layout is a very useful tool - more so in code. But the placement of code is like asking "how does one code an OOP app for auto rentals" or " how does one design a database for auto rentals". It not just an art, there are many answers. These are the "rules" I try to follow - YMMV.
To get started with this style of development I recommend checking out Let's Build That App as he goes through very in-depth examples of setting up complex apps entirely in code, without storyboards.
The way he structures the constraints is using a custom implementation of UIView, that way your view code is separated from the ViewController. Then, in the viewDidLoad method you can instantiate your implementation of UIView with something like self.view = MyView().
I wrote a few apps like this. The major drawbacks are that it can become very difficult to make quick adjustments, and you really need to learn about all the different types of constraints you can use.
Here's a pastebin of some extensions I used when doing this. I hope this helps.
OK, this may sound very basic (especially for someone who has written tens of thousands of Objective-C code), but I've always tried to avoid all this... or just tweak existing solutions. The result? I've never learnt how to do something simple like that.
So, here's my ultra-simple scenario:
I want to create a custom NSView (let's say a simple view with an image and a text in it), which I'll be able to assign in the Interface Builder (take an NSView element and set its class to MYCustomView - that's all - nothing more complicated)
I know I can write an NSView subclass and have it draw all my elements programmatically in drawRect: and all this - but I most definitely don't find any point in that.
What I do want is to simply draw the view in the Interface Builder (in our example, with a "placeholder" image and textfield), be able to use it as the "basis" of our NSView subclass, and also maintain pointers to the two elements in the view so that I can programmatically access them.
I know it's doable - I'm not asking about that. What I need is an ultra-simple walkthrough. Is there anything you can point me to?
Rephrasing the question in a... one-liner:
How can I replace the programmatic approach (seen in like 99.9% of NSView subclasses) in drawRect:, with a layout taken from a XIB?
P.S.
(A) Trust me, it must have been the 100th time I've been reading about NSViewControllers and all these, but not having used them, probably means that I still haven't found the point in using them...
(B) Please, don't shoot me with "what have you tried" questions. In the course of time, I've tried loads of things and at times I've somehow made it. However, it always feels like a crappy, messed up thing I just managed to get working. Nothing more, nothing less. All I want is to know if there is a simple tutorial on the above simple scenario.
(C) If I get an actual explanatory answer to this one, I guarantee I'll re-post it myself. You simply can't believe how many seasoned Cocoa developers have serious trouble dealing with this...
I've always wanted "custom" Storyboard classes as well!
This may not totally answer your question but this is just how we do it now, in iOS: just use container views.
Full extremely long tutorial: https://stackoverflow.com/a/23403979/294884
Everything's a container view in iOS now.
What we do is just have a scene, and then duplicate it: then change the colors or whatever as you describe.
Here's a literal example from the storyboard that was open behind this browser window!
Notice the small class/scene thing, we just copy it. Notice in the example it is slightly customised, just as you say. They are all the same class (it happens to be caled "BookBist") {"bist" == "bouncy list" btw}
Then as I say container views are the secret because, well, it's for exactly this purpose, it's why apple finally introduced "container views".
(BTW on that long container view tutorial. Search down to What if you want (say) a table controller or a page view controller instead of a UIViewController? it's a critical trick when making container views! Ridiculously Apple gives you a "default" VC when you drag in a container view; of course you never want that; in the example at hand I put the small BookBist scenes connected to the container views wherever they are needed.) Example...
Now, I 10000% understand what you are asking and have always wanted to know the answer myself!
For use HALF the answer, is, as I say, "copy the scene" so that works perfectly in modern storyboard. I appreciate that sucks, because what you want is a prefab, like in any game engine such as Unity3D, right? Me too!
But do note that THE OTHER HALF of your answer is certainly "container view magic" - "everything's" a container view now in iOS, indeed Apple finally put them in to make a rational way to do exactly the sort of thing you describe.
Is there any possible way to detect every change on User Interface during runtime??
I'm trying to find all objects in the current app interface.
I'm trying to to get all nodes inspecting recursively the main Window, but, for example, how to know if the top viewcontroller changes or if it's added a uiview dynamically, or is presented a modalview??
The main objective is to have a library to do this..
Any idea, help?
Thanks!
You could write your own library based on this, using advanced Objective-C techniques. I do not recommend you to do this, since it mostly breaks MVC patterns on iOS. Depends on what do you want to use it for, maybe analytics?
So these are the options I believe, if you want to actively inspect UIView hierarchy. All options are pretty complicated though.
Swizzle methods such as addSubview and removeFromSuperview of UIView, so you could know when changes like that happens. Including the getters of frame and bounds, if you wish to know the position.
You could use KVO to watch properties such as: subviews, frame, bounds, superview to notice any changes. But at one point you would have to add the same object as the observer (could be singleton).
Decide for an interval that is fired by a NSTimer and go through the hierarchy recursively beginning at keyWindow on UIApplication. This would have a big performance impact though.
There may be other options, but these are the ones I believe to be the best choices.
void invalidate()
Invalidates the entire screen.
void invalidateLayout()
Invalidates this screen's layout (including all controlled fields).
The javadoc doesn't help much. Could you tell me why do we need invalidateLayout()?
Depending on how you build your UI, you may be interested in invalidating, or updating layout, when you want the display to change.
If you have a UI that's composed of standard Field objects, like TextField, or ButtonField, or BrowserField, without much customization, then the work of defining how the UI looks is mostly in how those fields are laid out. Layout refers to setting the size of fields, and their position.
Sometimes, you'll have a UI where you need to deal at a lower level. For example, you may do some custom painting, which involves drawing lines, filling areas, drawing gradients, etc. This work is normally done in a Field object's paint() method. Sometimes, you also have drawing code in other places like drawFocus() or paintBackground().
If you want to trigger repainting, which calls your custom paint() method, then you want to use invalidate() to make that happen.
If you want to trigger a new layout, which arranges child fields with certain sizes, or positions, then invalidateLayout() can be used for that.
However, I will note that invalidateLayout() is a method in the Screen class. If you use a RIM Screen subclass, like MainScreen or FullScreen, then they come with their own top level delegate Manager objects. I believe that calling Screen.invalidateLayout() will trigger the sublayout() method on that built-in Manager to be called. That may not be what you really want.
Unless you implement your own Screen subclass like this:
public class MyScreen extends Screen {
public MyScreen() {
super(new MyCustomManager());
}
}
you probably aren't implementing the code (including sublayout()) for that delegate manager yourself.
I have found that when I want to force sublayout() to be called again, I trigger that with the updateLayout() method, not invalidateLayout(). Also, as opposed to invalidateLayout(), which is a method of the Screen, updateLayout is available for all Manager and Field classes, so you can choose just the specific part of the UI element tree that you want to refresh. But, that's just my experience.
Here's some comments on the BlackBerry forums on this topic
I started working as a iOS developer about a year and a half ago, and I'm having some trouble with software architecture and organization. I use Apple's recommended Model-View-Controller paradigm, and my code is generally very hierarchical: if a screen has (for example) a HUD, a control panel, and a display area, I have a main controller for the screen and sub-controllers for the HUD, control panel, and display area. The sub-controllers generally have no knowledge of their neighboring controllers and use methods in the main controller to interact with them.
However, especially in games, I often run into hierarchy-breaking problems that just can't be elegantly solved with this model. For instance, let's say I have a coin in the control panel area that I want to animate flying to the HUD. I can either animate the original coin to the new position, which would require a method like animateCoinToPosition: in the control panel sub-controller and a method like getPositionForFinalCoinPositionInHUD in the main controller; or, I can hide the original coin and create a duplicate coin either in the main controller or the HUD controller, which would require a delegate method like animateCoinToHUDFromStartingPosition:. I don't like having such oddly-specific methods in my controllers, since they only really exist to solve one problem, and additionally expose the hierarchy. My ideal solution would be to have a single method called animateCoinToHUD, but this would require flattening the entire hierarchy and merging the three controllers into one, which is obviously not worth it. (Or giving the sub-controllers access to their siblings — but that would essentially have the same effect. The sub-controllers would then have dependencies with each other, creating a single messy spiderweb controller instead of a main controller and three mostly independent sub-controllers.)
And it often gets worse. What if I want to display a full-screen animation or particle effect when moving the coin? What if my coin is a lot more complicated than a simple sprite, with many subviews and details, to the point where creating a duplicate coin using animateCoinToHUDFromStartingPosition: is inefficient? What if the coin flies to the HUD but then returns to the control panel? Do I "lend" the coin view to the main controller and then take it back when the animation completes, preserving the original position/z-order/etc. in temporary variables so that they can be restored? And another thing: logically, code that concerns multiple sub-controllers belongs in the main controller, but if these interactions are common, the main controller grows to be many thousands of lines long — which I've seen happen in many projects, not just my own.
Is there a consistent way to handle these hierarchy-breaking effects and actions that don't require duplicate code or assets, don't bloat my controllers, and elegantly allow me to share objects between sub-controllers? Or am I using the wrong approach entirely?
So, I think you may be thinking about the "never go up" the hierarchy a little too literally.
I think the idea is that you don't know specifically what the parent is, but you can define a protocol and know that whatever your parent object is it responds to said protocol. You ideally test in code to confirm that it responds to that protocol. Then use the protocol to send the message in a generic way wherein you pass the coin object to the parent object and let the parent object animate it off the screen and into the HUD.
The sub-controllers then have a id<parent_protocol> parent; instance variable and their initialization method takes one of those as a parameter. Given your description you already have something like this in place, or at least enough to implement "sub-controllers generally have no knowledge of their neighboring controllers and use methods in the main controller to interact with them" as you say.
So the idea, from a design perspective is that the coin pickup happens in the Display panel and all it knows is that it's parent object has a pickupCoin: method that will do whatever is appropriate with a picked up coin. The Display panel doesn't know it goes to the HUD, or whatever, just that picked up coins get handled by the parent controller's pickupCoin: method.
The OOP design philosophy here is that all knowledge of the parent is encapsulated in the protocol definition. This makes the child & parent more loosely coupled so that you could swap in any parent that implemented that protocol and the children would still work fine.
There are looser couplings you could use (globally posted notifications say), but in the cases you describe I think something like what I've outlined is probably more appropriate & likely more performant.
does that help?