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。

blazor 部署到Android blazor手机端_Software

选择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平台跑一下,宽屏,竖屏效果都对了

blazor 部署到Android blazor手机端_Software_02

 

blazor 部署到Android blazor手机端_css_03

 

DEMO代码地址:https://gitee.com/woodsun/mauiblazorapp