Upgrade to Pro — share decks privately, control downloads, hide ads and more …

同じアプリを Flutter と SwiftUI で書いてみる

同じアプリを Flutter と SwiftUI で書いてみる

Mobile勉強会 Wantedly × チームラボ × Sansan #14
https://wantedly.connpass.com/event/316051/

で発表した資料です

swiftty

May 21, 2024
Tweet

More Decks by swiftty

Other Decks in Programming

Transcript

  1. 今日話すこと iOS エンジニア視点で Flutter を学習した際に感じた 違いなどを SwiftUI の場合と比較しつつ感想を述べます ※ Flutter

    のコードはもっと良い書き方ができると思います こうするとよいなどあれば懇親会でぜひ教えて下さい 🙏
  2. 記事のデータ構造 "content": [ { "type": "heading", ... }, { "type":

    "paragraph", "inlineContent": [ { "type": "text", ... }, { "type": "reference", ... }, { "type": "image", ... }, ... ] }, { "type": "aside", "content": [...] }, ] 行ごとのデータ構造 paragraph などはイ ンライン要素でテキ ストやリンクなどの 種類を持つ
  3. enum BlockContent { case paragraph(Paragraph) case heading(Heading) case aside(Aside) ...

    struct Aside { public var style: String public var name: String? public var contents: [BlockContent] } } enum InlineContent { case text(Text) case codeVoice(CodeVoice) case strong(Strong) case emphasis(Emphasis) case image(Image) ... } @Freezed(unionKey: 'type') sealed class BlockContent with _$BlockContent { const factory BlockContent.heading({ required String text, required int level, String? anchor, }) = BlockContentHeading; const factory BlockContent.paragraph( List<InlineContent> inlineContent, ) = BlockContentParagraph; const factory BlockContent.aside({ required List<BlockContent> content, required String style, required String? name, }) = BlockContentAside; ... } @Freezed(unionKey: 'type', fallbackUnion: 'unknown') sealed class InlineContent with _$InlineContent { const factory InlineContent.text({ required String text, }) = InlineContentText; ... } Flutter は freezed を 用いてデータを定義 unionKey: 'type' で 直和型にマッピング 再帰的に BlockContent を持 つ →再帰的なデータに対 するビューを作る必要 がある 記事のデータ構造
  4. 1. 記事データの再帰的ビュー比較 Flutter では基底クラスの Widget を再帰的に返す構造で実現 Widget render(BlockContent block) {

    block.when( aside: (content) { return Container( padding: ..., decoration: ..., child: render(content), ); }, ... ); }
  5. SwiftUI では some View は実際は型がつくため再帰的なビューを作る ときは struct で型を確定させる必要がある // error:

    Function opaque return type was inferred as '...', // which defines the opaque type in terms of itself @ViewBuilder func render(_ block: BlockContent) -> some View { switch block { case .aside(let content): render(content) .padding() .border(.black) ... } } 1. 記事データの再帰的ビュー比較
  6. SwiftUI では some View は実際は型がつくため再帰的なビューを作る ときは struct で型を確定させる必要がある struct InnerView:

    View { let block: BlockContent var body: some View { switch block { case .aside(let content): InnerView(block: content) .padding() .border(.black) ... } } } 1. 記事データの再帰的ビュー比較
  7. リスト内包表記とスプレッド演算子を組み合わせると、要素の構造 を直感的に記述可能 if / for で返せる要素は一つだけだが、スプレッド演算子で囲むと複 数要素を扱える ListView( children: [

    for (final ref in detail.references) ...[ if (ref is ReferenceTopic) for (final fragment in ref.fragments) FragmentWidget(fragment) ], if (detail.relationshipsSections.isNotEmpty) ...[ const SizedBox(height: 8), const Divider(), ], ], 2. 子ビューの構築方法比較