MAUI Blazor学习1-移动客户端Shell布局
MAUI正式版发布半年了,Net 7也发布了,再次学习MAUI跨平台开发。UI类型选择Blazor,因为Html的生态圈比Xaml好太多了,能用Html解决的,就不要用Xaml。Blazor既可以开发网页客户端,又可以开发移动客户端,一个技术栈可以涵盖前后端,多平台,做小型工具软件不错。
开发环境Win10,Visual Studio 17.5.0 Preview 1.0,安卓模拟器Pixle 5 - API 30(Android 11.0 - API 30)。
新建项目
新建MAUI Blazor项目,选择位置D:\Software\gitee\mauiblazorapp。
选择Net 7.0框架。目标平台仅保留安卓和Windows。
D:\Software\gitee\mauiblazorapp\MaBlaApp\MaBlaApp.csproj
<TargetFrameworks>net7.0-android</TargetFrameworks>
<!--<TargetFrameworks>net7.0-android;net7.0-ios;net7.0-maccatalyst</TargetFrameworks>-->
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net7.0-windows10.0.19041.0</TargetFrameworks>
Shell布局
MAUI Blazor项目是网站布局,但是手机APP最常见的布局还是微信这种,Xamarin Forms项目可以直接选择Shell模板,MAUI Xaml项目也可以,但是MAUI Blazor项目不行,这真是一大遗憾。
幸好微软提供了一个移动APP布局的DEMO,这个项目是专门为了展示MAUI跨平台开发特性而设计的,有XAML和Blazor两个版本。
https://github.com/microsoft/dotnet-podcasts
打开其中的MAUI Blazor项目。
D:\Software\Test\dotnet-podcasts-main\src\MobileBlazor\mauiapp\NetPodsMauiBlazor.csproj
参照着DEMO的MainLayout.razor和css文件修改。对于不熟悉Html和css的.Net开发者而言,也是非常头大的工作。希望MAUI Blazor也能提供Shell布局的模板。
D:\Software\Test\dotnet-podcasts-main\src\Web\Pages\Shared\MainLayout.razor
D:\Software\Test\dotnet-podcasts-main\src\Web\Pages\Shared\MainLayout.razor.css
官网DEMO采用grid布局实现了Shell布局效果。这个其实可以参考Xaml的Grid布局或者StackLayout布局去理解,把一个窗口横着分几块,每一块里边再竖着分几块。手机小尺寸页面的情况下,横向分4块。PC大尺寸页面的情况下,横向分3块。
.page { background-color: var(--c-neutral-grey1); display: grid; grid-template-areas: "header" "main-view" "player-bar" "nav-bar"; grid-template-columns: 1fr; grid-template-rows: 56px 1fr auto auto; position: relative; height: 100%; width: 100%; } @media screen and (min-width: 992px) { .page { grid-template-areas: "header main-view" "nav-bar main-view" "player-bar player-bar"; grid-template-columns: auto 1fr; grid-template-rows: 142px 1fr auto; }
我写的DEMO很简单,手机小尺寸页面的情况下,横向分2块。PC大尺寸页面的情况下,横向分1块,相比dotnet-podcasts项目没有header和player-bar播放栏。简而言之,就只有nav-bar导航栏和main-view主视图,小尺寸页面上下排列,大尺寸页面左右排列。
D:\Software\gitee\mauiblazorapp\MaBlaApp\Shared\MainLayout.razor.css
.page { background-color: var(--c-neutral-grey1); display: grid; grid-template-areas: "main-view" "nav-bar"; grid-template-columns: 1fr; grid-template-rows: 1fr auto; position: relative; height: 100%; width: 100%; } @media screen and (min-width: 992px) { .page { grid-template-areas: "nav-bar main-view"; grid-template-columns: auto 1fr; grid-template-rows: 1fr; }
然后再借鉴dotnet-podcasts项目的菜单样式。
D:\Software\Test\dotnet-podcasts-main\src\Web\Pages\Shared\NavMenu.razor
D:\Software\Test\dotnet-podcasts-main\src\Web\Pages\Shared\NavMenu.razor.css
.navbarApp__wrapper { display: grid; grid-template-columns: repeat(5, 1fr); }
我写的DEMO减少为3个按钮
.navbarApp__wrapper { display: grid; grid-template-columns: repeat(3, 1fr); }
3个按钮就是Blazor项目模板自带的
D:\Software\gitee\mauiblazorapp\MaBlaApp\Shared\NavMenu.razor
<div class="navbarApp"> <nav class="navbarApp__wrapper"> <div class="navbarApp-item"> <NavLink class="navbarApp-link" href="" Match="NavLinkMatch.All"> <span class="navbarApp-linkIcon oi oi-home" aria-hidden="true"></span> <span class="navbarApp-linkLabel">Home</span> </NavLink> </div> <div class="navbarApp-item"> <NavLink class="navbarApp-link" href="counter"> <span class="navbarApp-linkIcon oi oi-plus" aria-hidden="true"></span> <span class="navbarApp-linkLabel">Counter</span> </NavLink> </div> <div class="navbarApp-item"> <NavLink class="navbarApp-link" href="fetchdata"> <span class="navbarApp-linkIcon oi oi-list-rich" aria-hidden="true"></span> <span class="navbarApp-linkLabel">Fetch data</span> </NavLink> </div> </nav> </div> @code { }
把dotnet-podcasts项目的字体也搬过来
D:\Software\Test\dotnet-podcasts-main\src\Web\Components\wwwroot\css\fonts
放在wwwroot\css\fonts
D:\Software\gitee\mauiblazorapp\MaBlaApp\wwwroot\css\fonts
照搬dotnet-podcasts项目的字体、颜色定义
D:\Software\Test\dotnet-podcasts-main\src\Web\Components\wwwroot\css\styles.css
放在app.css里边
D:\Software\gitee\mauiblazorapp\MaBlaApp\wwwroot\css\app.css
@font-face { font-display: swap; font-family: "Segoe UI Light"; font-style: normal; font-weight: 100; src: url("fonts/SegoeUILight.woff") format("woff"); } @font-face { font-display: swap; font-family: "Segoe UI Semilight"; font-style: normal; font-weight: 300; src: url("fonts/SegoeUISemilight.woff") format("woff"); } @font-face { font-display: swap; font-family: "Segoe UI"; font-style: normal; font-weight: 400; src: url("fonts/SegoeUI.woff") format("woff"); } @font-face { font-display: swap; font-family: "Segoe UI Semibold"; font-style: normal; font-weight: 600; src: url("fonts/SegoeUISemibold.woff") format("woff"); } @font-face { font-display: swap; font-family: "Segoe UI Bold"; font-style: normal; font-weight: 700; src: url("fonts/SegoeUIBold.woff") format("woff"); } body { --c-primary: #c00cc0; --c-primary-dark: #8b0995; --c-primary-light: #dd0eb3; --c-primary-xlight: #fcf2f7; --c-primary-xxlight: #fcf2f4; --c-secondary: #e10890; --c-neutral-black: #000000; --c-neutral-dark: #1f1f1f; --c-neutral-grey1: #faf9f8; --c-neutral-grey2: #f3f2f1; --c-neutral-grey3: #edebe9; --c-neutral-grey4: #d2d0ce; --c-neutral-grey5: #c8c6c4; --c-neutral-grey6: #a19f9d; --c-neutral-grey7: #605e5c; --c-neutral-grey8: #3b3a39; --c-neutral-grey9: #323130; --c-neutral-white: #ffffff; --c-neutral-white-featured: #ffffff; --c-neutral-white-complementary: #ffffff; --c-player-range: #605e5c; --c-waves-image: #f3f2f1; --c-searchbar: transparent; --c-switch: #605e5c; --c-switch-dark: #1f1f1f; --c-category: #f3f2f1; --gradient-primary-color: #c00cc0; --gradient-secondary-color: #e10890; --gradient-final: radial-gradient( 101.22% 790.99% at 0% 3.41%, #c00cc0 0%, #e10890 100% ); --gradient-splash: linear-gradient(114.18deg, #bf26f5 0%, #e2068c 100%); --ff-light: "Segoe UI Light"; --ff-semilight: "Segoe UI Semilight"; --ff-regular: "Segoe UI"; --ff-semibold: "Segoe UI Semibold"; --ff-bold: "Segoe UI Bold"; --headlines-h1-fs: 90px; --headlines-h1-lh: 96px; --headlines-h2-fs: 64px; --headlines-h2-lh: 72px; --headlines-h3-fs: 48px; --headlines-h3-lh: 56px; --headlines-h4-fs: 36px; --headlines-h4-lh: 44px; --headlines-h5-fs: 32px; --headlines-h5-lh: 40px; --headlines-h6-fs: 24px; --headlines-h6-lh: 36px; --subheadings-s1-fs: 36px; --subheadings-s1-lh: 44px; --subheadings-s2-fs: 32px; --subheadings-s2-lh: 42px; --subheadings-s3-fs: 24px; --subheadings-s3-lh: 40px; --subheadings-s4-fs: 22px; --subheadings-s4-lh: 32px; --subheadings-s5-fs: 20px; --subheadings-s5-lh: 30px; --subheadings-s6-fs: 18px; --subheadings-s6-lh: 28px; --text-xxl-fs: 24px; --text-xxl-lh: 32px; --text-xl-fs: 20px; --text-xl-lh: 30px; --text-l-fs: 18px; --text-l-lh: 28px; --text-m-fs: 16px; --text-m-lh: 24px; --text-s-fs: 14px; --text-s-lh: 22px; --text-xs-fs: 12px; --text-xs-lh: 18px; --link-xl-fs: 20px; --link-xl-lh: 30px; --link-l-fs: 18px; --link-l-lh: 28px; --link-m-fs: 16px; --link-m-lh: 24px; --link-s-fs: 14px; --link-s-lh: 22px; --input-fs: 15px; --input-lh: 24px; --box-shadow-s: 0 1px 4px rgba(0, 0, 0, 0.15), 0 1px 1px rgba(0, 0, 0, 0.05); --box-shadow-m: 0 6px 12px rgba(0, 0, 0, 0.1), 0 1px 4px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(0, 0, 0, 0.05); --box-shadow-l: 0 10px 24px rgba(0, 0, 0, 0.08), 0 12px 16px rgba(0, 0, 0, 0.08), 0 4px 8px rgba(0, 0, 0, 0.03); --box-shadow-xl: 0 30px 48px rgba(0, 0, 0, 0.16), 0 8px 30px rgba(0, 0, 0, 0.14), 0 8px 10px rgba(0, 0, 0, 0.1), 0 4px 12px -10px rgba(0, 0, 0, 0.5); } html, body, #app, .page { height: 100%; }
测试效果
在Windows平台跑一下,宽屏,竖屏效果都对了
DEMO代码地址:https://gitee.com/woodsun/mauiblazorapp