https://github.com/xamarin/Xamarin.Forms/pull/6764
The core idea of this API is to expose the active structural state of the Shell as a model class that can be manipulated and applied to the shell.
Used to indicate the entire routing state of the shell. This contains all the branched stacks, the different paths that represent each different page, the paramteres to apply to those pages, how to transition to and away from those. The idea here is that you can setup an entire route state based on this data. All of this could be serialized during tombstoning as well to fully recreate the application. The renderer level can also take this in to see where the user is going and then at that point make decisions about what it wants to present and how
```C#
public class ShellRouteState
{
public RoutePath[] Routes { get; }
public RoutePath CurrentRoute { get; }
}
### Properties
| API | Description |
| ------------- | ------------- |
| Routes | Flyout items effectively cause you to swap stacks so this is a way to represent those different stacks. |
| ActiveRoute | this represents the visible stack. |
---
### RoutePath
Signifies an individual stack path
```C#
public class RoutePath
{
public RoutePath(IList<PathPart> pathParts)
{
PathParts = new ReadOnlyCollection<PathPart>(pathParts);
}
public IReadOnlyList<PathPart> PathParts { get; }
public Dictionary<string, string> NavigationParameters { get; }
}
| API | Description |
| ------------- | ------------- |
| PathParts | List of path parts representing different pages on the stack. |
| NavigationParameters | The parameters to apply at the shell level. |
Signifies a specific part of the route that applies to a given shell element being navigated to. This also contains the parameters that will be applied to the particular shell part
```C#
public class PathPart
{
public string Path { get; }
// in theory you could store a bindingcontext here for tombstoning
public IEnumerable<KeyValuePair<string, object>> Parameters { get; }
public Element ShellPart { get; }
// This describes how you will transition to and away from this Path Part
ITransitionPlan Transition { get; }
// how am I presented? Modally? as a page?W
PresentationHint Presentation { get; }
}
### Properties
| API | Description |
| ------------- | ------------- |
| Path | The path associated with this Shell Part. |
| Parameters | List of parameters to apply to the realized content. |
| Transition | How to transition to this page when it's added and when it's removed. |
| Presentation | Am I modal? A dialog box? other? |
| ShellPart | Element associated with this path of the Uri |
## Interfaces
### IUriProjectionParser
- Allows a developer to implement a custom uri scheme. The uri could completely demolish the current state and just return a new setup completely
```C#
public interface IUriProjectionParser
{
Task<ShellRouteState> ParseAsync(ShellUriParserArgs args);
}
#### IShellContentCreator
- Customize how a content page is created
```C#
public interface IShellContentCreator
{
ContentPage Create(ShellContentCreateArgs content);
}
#### IShellPartAppearing
- Do things before the Shell<part> is appearing but the intention for it to appear is definite and nothing can stop it. This is called for ShellItem, Section, Content.
```C#
public interface IShellPartAppearing
{
Task AppearingAsync(ShellLifecycleArgs args);
}
```C#
public interface IShellPartAppeared
{
Task Appeared(ShellLifecycleArgs args);
}
#### IShellPartDisappeared
- Shell part has been navigated away from and is no longer visible
```C#
public interface IShellPartDisappeared
{
Task DisappearedAsync(ShellLifecycleArgs args);
}
Arguments passed to Navigating process indicating where I'm going to be going
```C#
public class ShellNavigationArgs : EventArgs
{
public Shell Shell { get; }
public ShellRouteState FutureState { get; }
}
### Properties
| API | Description |
| ------------- | ------------- |
| Shell | The current shell. |
| FutureState | The state being changed to. |
---
### ShellContentCreateArgs
Arguments passed to Shell Content creation.
```C#
public class ShellContentCreateArgs : EventArgs
{
public ShellContent Content { get; }
}
| API | Description |
| ------------- | ------------- |
| Content | The Shell Content being created. |
Arguments passed to Method that parses Uri to New Route Path
```C#
public class ShellUriParserArgs : EventArgs
{
public Shell Shell { get; }
public Uri Uri { get; }
}
### Properties
| API | Description |
| ------------- | ------------- |
| Shell | The Current Shell. |
| Uri | The Uri being navigated to. |
---
### ShellLifecycleArgs
Arguments passed to various stages related to life cycle.
```C#
public class ShellLifecycleArgs : EventArgs
{
public Element Element { get; }
public PathPart PathPart { get; }
public RoutePath RoutePath { get; }
public bool IsLast { get; }
}
| API | Description |
| ------------- | ------------- |
| Element | Element event is happening to. |
| PathPart | The part of the path that's connected to this Element . |
| RoutePath | The full path. |
| IsLast | Is this the last thing being routed to. |
```C#
public class CustomNavigationServices : ShellNavigationService
{
ViewModelBase _navigatingViewModel;
public CustomNavigationServices()
{
}
public async Task GotoAsync(ViewModelBase viewModelBase)
{
_navigatingViewModel = viewModelBase;
await Shell.Current.GoToAsync($"//{viewModelBase.GetType().Name}", true);
}
public override Task<ShellRouteState> ParseAsync(ShellUriParserArgs args)
{
//provide custom logic here to parse the uri being navigated to
if(args.Uri.ToString() == "..")
return args.Shell.CurrentRouteState().Pop();
return base.ParseAsync(args);
}
public override void ApplyParameters(ShellLifecycleArgs args)
{
base.ApplyParameters(args);
}
public override Page Create(ShellContentCreateArgs args)
{
var content = base.Create(args);
if (args.Content.Route == "HomePageViewModel")
content.BindingContext = _navigatingViewModel as HomePageViewModel ?? new HomePageViewModel();
else if (args.Content.Route == "LoginPageViewModel")
content.BindingContext = _navigatingViewModel as LoginPageViewModel ?? new LoginPageViewModel();
_navigatingViewModel = null;
return content;
}
}
#### Custom Navigation Service example
The *ShellRoutePath* can be interacted with in the same way as you would any other navigation
```C#
public override async Task<ShellRouteState> ParseAsync(ShellUriParserArgs args)
{
ShellRouteState returnValue = shell.GetCurrentRouteState();
// Pop current page off stack
returnValue.Pop();
returnValue.RemoveRoute("routename");
returnValue.AddRouteWithParameters("routename", parameterstopassing);
return returnValue;
}
Existing work being done here please comment!!
https://github.com/xamarin/Xamarin.Forms/pull/6542
Currently there is no good way of easily integrating an App's framework into Shell. To make this easier a Handler should be provided so that a Framework can simply provide Forms a custom implementation for the core Navigation functionality.
These methods should all be exposed though an Interface so that it could be potentially implemented in a stand alone service or on the Application itself since this is where you would most likely have a direct reference to a DI Container.
To allow frameworks like Prism to more easily integrate with Shell.
I'm stuck on the same issue. I have a simple mvvm framework that I use and with the navigationpage I was able to integrate it in my framework so I could navigate from my viewmodel and respond to navigation events.
@davidortinau as discussed at the MVP Summit this is a major blocker to any possible adoption of Shell by third party frameworks like Prism. Always happy to jump on a call with you and the team if it would help to clarify understanding around the issues.
We are having similar issues to @dansiegel -- on the ReactiveUI MVVM framework we are delayed until the change are made.
Thanks @dansiegel. FreshMvvm requires this also. Is this implementation being discussed?
In FreshMvvm we are able to support the shell/url based navigation in Shell. We just need all the information in events when pushing back and forth.
When navigating forward ideally:
-Current/Previous Page Instance
-New Page Type+Instance (The page that's being pushed)
-If pushed as model
-The data that's being pushed
It would be nice to have the actual navigation handling decoupled from shell. It could be an interface that comes with a default implementation.
Prism, FreshMvvm could implement their own and assign it to the Shell instance. An INavigator of sorts.
And I would be ecstatic if I could implement something like flutters return values with it. (https://flutter.dev/docs/get-started/flutter-for/xamarin-forms-devs#how-do-i-navigate-between-pages)
Map coordinates = await Navigator.of(context).pushNamed('/location');
With regards to DI and BindingContexts it would be awesome to have it like Blazor: https://docs.microsoft.com/en-us/aspnet/core/blazor/dependency-injection?view=aspnetcore-3.0 and simply control it using attributes.
@puppetSpace @timahrentlov @rid00z @glennawatson @dansiegel @TimLariviere
Updated description with some specs and a link to ongoing work against this spec.
Have it 80 percent working but need to smooth out a few more cases. Once there's a nuget I'll attach it to the top of this. Mileage may vary as I haven't super tested it all as I want to start feedback loop first before solidifying.
@PureWeen I'm not sure to understand how these API changes enable customizing the created view based on the URI being navigated to.
IShellContentCreator
seems only to provide a way to override how the view is created, not react to a given route path.
When a navigation is required, it would be great to have an endpoint where we can have Shell give us the URI (and the parsed parameters) so we can decide which view to return.
Xamarin.Forms.View NavigatingTo(string uriPath, Dictionary<string, string> parameters)
Inheriting+overriding Shell might be a limited option for some frameworks (e.g. Fabulous)
Typically, what I would like to achieve with Fabulous is to pattern match on routes to give the corresponding view.
View.Shell(
// Existing Items property
items=[
View.FlyoutItem(...)
View.MenuItem(...)
],
// Routing
routes=(fun route params ->
match route with
| "ABC" -> View.ContentPage(title=params.["name"])
| "XYZ" -> DetailsPage.view params ...
)
)
@TimLariviere
So that's kind of what this fellow here is for
https://github.com/xamarin/Xamarin.Forms/pull/6383/files#diff-417fee52142bda9a82e0a134e063185bR68
This basically takes in a Uri/Current Shell State and then it returns a ShellRouteState that represents what the new Shell will look like.
The plan is to additionally add helpers, events, etc... to make some of these data structures easier to interact with so maybe that will help your scenario. For example IUriProjectionParser will have an event where you can influence it the same way as the DependencyResolver if you're not really wanting to create an interface.
So with your matching what I would envision is that
F#
routes=(fun route params ->
match route with
| "ABC" -> View.ContentPage(title=params.["name"])
| "XYZ" -> DetailsPage.view params ...
)
Would return a modified version of this
https://github.com/xamarin/Xamarin.Forms/pull/6383/files#diff-417fee52142bda9a82e0a134e063185bR126
representing how the current route state is changing. That structure also supports setting the parameters each particular part of the path as well so that once those parts of the path are realized those sets of parameters will get set
Typically, what I would like to achieve with Fabulous is to pattern match on routes to give the corresponding view.
Here we might be mismatched on our definition of "view" so I'm taking "view" to mean the ContentPage (let me know if that's wrong?)
The thing with shell is that there's not necessarily just a "view" that is associated with a Route. The route is more setting the state of the shell. What shell item is active? what tabs are visible? etc.... so there's not really a specific mapping from a uri to a page. The URI describes what part of the shell is activated and then in theory MVVM frameworks could build there own URI processing engines to even build the shell itself
@PureWeen in general I like where you're going. I do agree some of these tasks can and should live as their own interfaces however this does seem overly broken up.
These in particular should be a single interface like IShellPartLifecycle
If anyone wants to start playing with the API for this I will keep the nuget over here updated
https://github.com/xamarin/Xamarin.Forms/pull/6764
@PureWeen great work so far on this Issue.
Is there an Update on the progress of this Issue or could you provide an update to the Nuget #6764 if this Issue doesn't make it into the pre3?
You're Nuget is working great on Android, but on iOS i get an "CurrentItem is null" Exception when the AppShell is getting initialized.
The developers have to choose either their favourite framework or shell until this issue is resolved. When should we expect this to be completed.
Hi XF team. When the features will be available and released? Or any plan. We have a project in which I want to use Prism and Shell. The currently Shell page-to-page navigation and the way parameters are passing during navigation are really a blocker for us. We love Shell. It makes the app structure so simple and graceful.
How would you go about implementing something akin to Prism's IDestructible interface?
What is the mechanism by which the page instance itself are told that it is no longer used or that all shell references to it has been dropped ?
@dansiegel or @timahrentlov what are the conditions met that causes Destroy() to get called in Prism?
Just want to make sure I'm answering @timahrentlov 's question correctly :-)
@PureWeen IDestructible
is called when the page is popped and will not be navigated back to. It's one of the things I believe we were starting to look at the last time we streamed.
@dansiegel that's what I thought
So @timahrentlov basically that :-)
The ShellRouteState
data structures contains a references to literally everything.
If you have 12 tabs going each with different navigation stacks that'll be inside that ShellrouteState
structure
At the point anything gets removed from that ShellRouteState
structure it'll have something like IDestructible
that gets called.
Excellent! Thanks @dansiegel and @PureWeen
For the time being this also limits using mobile Blazor bindings with external frameworks as that projects seems to plan support only for Shell.
Most helpful comment
Hi XF team. When the features will be available and released? Or any plan. We have a project in which I want to use Prism and Shell. The currently Shell page-to-page navigation and the way parameters are passing during navigation are really a blocker for us. We love Shell. It makes the app structure so simple and graceful.