Figmaで作る.NET MAUI UIコンポーネント

# はじめに
先週YouTuberの記事を書きましたが、その中で紹介したJavier Suárezさんが作ったFigmaのデザインをそのまま.NET MAUIに持ってくるツールを使ってみたので、その紹介をします。

他の記事で導入方法については書いていますので、ここではインストール後からお話できればと思います。

# Figma Design to .NET MAUI
さて、ここからが本題になります。
まず、インストールしたFigma.Maui.Graphics.Sampleを開きます。
![sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3007605/16201d96-8bc6-a29f-473f-298333f772a0.png)

ここに赤の線のところにコピーしたFigmaのアクセストークンを貼り付けます。

そして黄色いところにはデザインを持っていきたいFigmaFileのURLのFileID部分をコピペします。
![YourFi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3007605/eab13bd7-ea28-c9f7-7234-976349b2c262.png)

「Generate」を押すとC#のコードが作成されるので「Export」して保存してください。

# .NET MAUIで実装
新規作成をして生成したコードを表示するGraphicsViewを作成します。

MAUIのテンプレートを新規作成して「MainPage.xaml」を以下のように書き換えます。
```xml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="FigmaApps.MainPage">
    <ScrollView>
            <GraphicsView //GraphicsViewの追加  
                x:Name="figmaSample"/>
    </ScrollView>
</ContentPage>
```

そして「MainPage.xaml.cs」は以下のように書き換えます。
```cs
namespace FigmaApps;

public partial class MainPage : ContentPage
{
	public MainPage()
	{
		InitializeComponent();

        figmaSample.Drawable = new Drawable();//x:Nameと名前をあわせること
	}

	class Drawable:IDrawable //IDrawblewを継承したDrawbleクラスを実装
	{
		public void Draw(ICanvas canvas, RectF dirtyRect)
        {
            //ここに生成したコードをコピペする  ここに書いてあるコードをどう編集していいかはさすがにわかってると思うので割愛
            /////////////////////////////////////////////////////////////////////////////////
            // View:     rectangleView
            // NodeName: Rectangle 1
            // NodeType: RECTANGLE
            // NodeId:   1:4
            canvas.SaveState();
            canvas.SetFillPaint(new LinearGradientPaint { GradientStops = new PaintGradientStop[] { new PaintGradientStop(0, new Color(234, 234, 234, 255)), new PaintGradientStop(1, new Color(234, 234, 234, 130)), } }, new RectF(0f, 0f, 350f, 500f));
            canvas.FillRoundedRectangle(0f, 0f, 350f, 500f, 10f);
            canvas.StrokeColor = Color.FromRgb(0, 0, 0);
            canvas.Alpha = 1;
            canvas.StrokeSize = 1;
            canvas.DrawRoundedRectangle(0f, 0f, 350f, 500f, 10f);
            canvas.RestoreState();

            // View:     rectangleView1
            // NodeName: Rectangle 2
            // NodeType: RECTANGLE
            // NodeId:   1:5
            canvas.SaveState();
            canvas.SetFillPaint(new LinearGradientPaint { GradientStops = new PaintGradientStop[] { new PaintGradientStop(0, new Color(81, 43, 212, 255)), new PaintGradientStop(1, new Color(43, 151, 212, 41)), } }, new RectF(0f, 430f, 350f, 5f));
            canvas.FillRoundedRectangle(0f, 430f, 350f, 5f, 0f);
            canvas.RestoreState();

            // View:     rectangleView2
            // NodeName: Rectangle 3
            // NodeType: RECTANGLE
            // NodeId:   1:7
            canvas.SaveState();
            canvas.SetFillPaint(new LinearGradientPaint { GradientStops = new PaintGradientStop[] { new PaintGradientStop(0, new Color(81, 43, 212, 255)), new PaintGradientStop(1, new Color(81, 43, 212, 89)), } }, new RectF(14f, 446f, 325f, 41f));
            canvas.FillRoundedRectangle(14f, 446f, 325f, 41f, 20f);
            canvas.RestoreState();

            // View:     elipseView
            // NodeName: Ellipse 1
            // NodeType: ELLIPSE
            // NodeId:   1:8
            canvas.SaveState();
            canvas.SetFillPaint(new LinearGradientPaint { GradientStops = new PaintGradientStop[] { new PaintGradientStop(0, new Color(81, 43, 212, 255)), new PaintGradientStop(1, new Color(43, 151, 212, 41)), } }, new RectF(19f, 28f, 116f, 116f));
            canvas.StrokeSize = 5;
            canvas.DrawEllipse(19f, 28f, 116f, 116f);
            canvas.RestoreState();

            // View:     textView
            // NodeName: Name
            // NodeType: TEXT
            // NodeId:   1:9
            canvas.SaveState();
            canvas.FontColor = Color.FromRgb(0, 0, 0);
            canvas.Alpha = 1;
            canvas.Font = new Microsoft.Maui.Graphics.Font("Shrikhand-Regular", 400, FontStyleType.Normal);
            canvas.FontSize = 16f;
            canvas.DrawString(@"Name", 175f, 28f, 63f, 23f, HorizontalAlignment.Left, VerticalAlignment.Top);
            canvas.RestoreState();

            // View:     rectangleView3
            // NodeName: Rectangle 4
            // NodeType: RECTANGLE
            // NodeId:   1:10
            canvas.SaveState();
            canvas.SetFillPaint(new RadialGradientPaint { GradientStops = new PaintGradientStop[] { new PaintGradientStop(0, new Color(81, 43, 212, 255)), new PaintGradientStop(1, new Color(139, 117, 214, 255)), new PaintGradientStop(1, new Color(217, 217, 217, 255)), } }, new RectF(30f, 200f, 50f, 50f));
            canvas.FillRoundedRectangle(30f, 200f, 50f, 50f, 10f);
            canvas.RestoreState();

            // View:     rectangleView4
            // NodeName: Rectangle 5
            // NodeType: RECTANGLE
            // NodeId:   1:11
            canvas.SaveState();
            canvas.SetFillPaint(new RadialGradientPaint { GradientStops = new PaintGradientStop[] { new PaintGradientStop(0, new Color(43, 151, 212, 255)), new PaintGradientStop(1, new Color(43, 151, 212, 0)), } }, new RectF(30f, 270f, 50f, 50f));
            canvas.FillRoundedRectangle(30f, 270f, 50f, 50f, 10f);
            canvas.RestoreState();

            // View:     rectangleView5
            // NodeName: Rectangle 6
            // NodeType: RECTANGLE
            // NodeId:   1:12
            canvas.SaveState();
            canvas.FillColor = Color.FromRgb(163, 242, 143);
            canvas.Alpha = 1;
            canvas.FillRoundedRectangle(30f, 340f, 50f, 50f, 10f);
            canvas.RestoreState();

            // View:     imageView
            // NodeName: Vector 1
            // NodeType: VECTOR
            // NodeId:   1:16

            // View:     textView1
            // NodeName: MAUI
            // NodeType: TEXT
            // NodeId:   1:17
            canvas.SaveState();
            canvas.FontColor = Color.FromRgb(0, 0, 0);
            canvas.Alpha = 1;
            canvas.Font = new Microsoft.Maui.Graphics.Font("Shrikhand-Regular", 400, FontStyleType.Normal);
            canvas.FontSize = 16f;
            canvas.DrawString(@"MAUI", 88f, 184f, 47f, 23f, HorizontalAlignment.Left, VerticalAlignment.Top);
            canvas.RestoreState();

            // View:     textView2
            // NodeName: .Net MAUI High School
            // NodeType: TEXT
            // NodeId:   1:22
            canvas.SaveState();
            canvas.FontColor = Color.FromRgb(0, 0, 0);
            canvas.Alpha = 1;
            canvas.Font = new Microsoft.Maui.Graphics.Font("Rakkas-Regular", 400, FontStyleType.Normal);
            canvas.FontSize = 16f;
            canvas.DrawString(@".Net MAUI High School", 96f, 213f, 153f, 24f, HorizontalAlignment.Left, VerticalAlignment.Top);
            canvas.RestoreState();

            // View:     textView3
            // NodeName: TwitterAccount
            // NodeType: TEXT
            // NodeId:   1:24
            canvas.SaveState();
            canvas.FontColor = Color.FromRgb(0, 0, 0);
            canvas.Alpha = 1;
            canvas.Font = new Microsoft.Maui.Graphics.Font("Rakkas-Regular", 400, FontStyleType.Normal);
            canvas.FontSize = 16f;
            canvas.DrawString(@"TwitterAccount", 96f, 289f, 103f, 24f, HorizontalAlignment.Left, VerticalAlignment.Top);
            canvas.RestoreState();

            // View:     textView4
            // NodeName: QiitaAccount
            // NodeType: TEXT
            // NodeId:   1:25
            canvas.SaveState();
            canvas.FontColor = Color.FromRgb(0, 0, 0);
            canvas.Alpha = 1;
            canvas.Font = new Microsoft.Maui.Graphics.Font("Rakkas-Regular", 400, FontStyleType.Normal);
            canvas.FontSize = 16f;
            canvas.DrawString(@"QiitaAccount", 96f, 358f, 87f, 24f, HorizontalAlignment.Left, VerticalAlignment.Top);
            canvas.RestoreState();

            // View:     textView5
            // NodeName: Twitter
            // NodeType: TEXT
            // NodeId:   1:18
            canvas.SaveState();
            canvas.FontColor = Color.FromRgb(0, 0, 0);
            canvas.Alpha = 1;
            canvas.Font = new Microsoft.Maui.Graphics.Font("Shrikhand-Regular", 400, FontStyleType.Normal);
            canvas.FontSize = 16f;
            canvas.DrawString(@"Twitter", 88f, 254f, 64f, 23f, HorizontalAlignment.Left, VerticalAlignment.Top);
            canvas.RestoreState();

            // View:     textView6
            // NodeName: Qiita
            // NodeType: TEXT
            // NodeId:   1:20
            canvas.SaveState();
            canvas.FontColor = Color.FromRgb(0, 0, 0);
            canvas.Alpha = 1;
            canvas.Font = new Microsoft.Maui.Graphics.Font("Shrikhand-Regular", 400, FontStyleType.Normal);
            canvas.FontSize = 16f;
            canvas.DrawString(@"Qiita", 88f, 324f, 41f, 23f, HorizontalAlignment.Left, VerticalAlignment.Top);
            canvas.RestoreState();
        }
    }
}
```
![Figmaで作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3007605/64263407-de93-4cb3-2194-045e5fda9aba.png)

これで完了になります。

# Visual StudioはIDEでありTeacherです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3007605/353674a1-b151-d681-ad7e-e7746fed4991.png)
影がないなと思ったらつければいいのです。
shadowをつけようと思ったら勝手にSetShadowが出てきてその引数が何のかさえ教えてくれます。
こんな感じでVisual Studioをうまく使っていきましょう。先生最高！！

# 不具合など
見たらわかると思いますが、変換がうまくいってない箇所が多少見受けられます。
それらの理由や対処法などご紹介できればと思います。

・Elipeseのグラデーションができない。→ソフト的には未対応。しかしコードを書き替えることによって対応は可能
・ドロップシャドウがついてない→ソフト的には未対応。しかしコードを付け足すことによって対応は可能(canvas.SetShadow())
・パスが描かれない→ソフト的に未対応詳細はこちら(https://github.com/jsuarezruiz/figma-to-maui-graphics#known-limitations-or-issues)
・不具合ではないですがコンポーネントのX,Yの値を0にそろえていないとうまく変換されないようです。↑参照
・フォントが変わっていない→.NET MAUIプロジェクトに対応するﾌｫﾝﾄを入れ込めば反映可能
・3つボタンの上2つがうまく変換できていない→下の図のように決められたサイズの枠からはみ出してコンポーネントを作ったための不具合。きちんとルールにのっとれば可能
![gurade-syonn.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3007605/f15dce5f-4451-3aac-870f-692d79ae44eb.png)

# さいごに
以上がFigmaのデザインを.NET MAUIへ持ってくる方法の紹介となりました。

多少の不具合はありますがこんなに簡単にFigmaで作ったデザインをモバイルアプリのUIを構築できる方法は今のところないと思います。
(あっても多少のお金がかかったり、操作が大変であったり)

今回私が自作したデザインで試しましたが、ほかのFigmaユーザーが作成したデザインであってもコピーを取得し、URLさえ入手できればどのものでも生成が可能です。

UIコンポーネントくらいならかなり良い感じで爆速で生成ができますね。