Skip to content

Commit

Permalink
Flutter Webによるdocsビューワの作成
Browse files Browse the repository at this point in the history
  • Loading branch information
mj-hd committed Aug 1, 2024
1 parent 762f08c commit 6fd2e8c
Show file tree
Hide file tree
Showing 24 changed files with 979 additions and 0 deletions.
43 changes: 43 additions & 0 deletions apps/website/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/

# IntelliJ related
*.iml
*.ipr
*.iws
.idea/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/

# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/

# Symbolication related
app.*.symbols

# Obfuscation related
app.*.map.json

# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
30 changes: 30 additions & 0 deletions apps/website/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: "a14f74ff3a1cbd521163c5f03d68113d50af93d3"
channel: "stable"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
- platform: web
create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3

# User provided section

# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
4 changes: 4 additions & 0 deletions apps/website/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Website

This is a web document viewer.
You can view the markdown documents in the `docs/` directory by this app.
6 changes: 6 additions & 0 deletions apps/website/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
include: package:yumemi_lints/flutter/3.22/recommended.yaml

linter:
rules:
# Webを前提とするプロジェクトなので無効化
avoid_web_libraries_in_flutter: false
1 change: 1 addition & 0 deletions apps/website/assets/docs
22 changes: 22 additions & 0 deletions apps/website/lib/hooks/use_markdown_doc.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:website/utils/doc_key.dart';

String? useMarkdownDoc(String? docKey) {
final doc = useState<String?>(null);

useEffect(
() {
if (docKey != null) {
Future.microtask(() async {
doc.value = await rootBundle.loadString(toAssetKey(docKey));
});
}

return;
},
[docKey],
);

return doc.value;
}
27 changes: 27 additions & 0 deletions apps/website/lib/hooks/use_markdown_doc_keys.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:website/utils/doc_key.dart';

List<String> useMarkdownDocKeys() {
final docs = useState<List<String>>([]);

useEffect(
() {
Future.microtask(() async {
final manifest = await AssetManifest.loadFromAssetBundle(rootBundle);
final markdowns = manifest
.listAssets()
.where((key) => key.endsWith('.md'))
.map(toDocKey)
.toList();

docs.value = markdowns;
});

return;
},
[],
);

return docs.value;
}
22 changes: 22 additions & 0 deletions apps/website/lib/main.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import 'package:flutter/material.dart';
import 'package:website/router/router.dart';

void main() {
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'Documents',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
routerConfig: router,
);
}
}
13 changes: 13 additions & 0 deletions apps/website/lib/router/router.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:website/ui/default_layout.dart';
import 'package:website/ui/markdown_page.dart';

part 'package:website/router/routes/markdown_page_route.dart';
part 'package:website/router/routes/markdown_shell_route.dart';
part 'router.g.dart';

final router = GoRouter(
initialLocation: '/',
routes: $appRoutes,
);
48 changes: 48 additions & 0 deletions apps/website/lib/router/router.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions apps/website/lib/router/routes/markdown_page_route.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
part of 'package:website/router/router.dart';

class MarkdownPageRoute extends GoRouteData {
const MarkdownPageRoute({
this.key,
});

static const path = '/';

final String? key;

@override
Widget build(BuildContext context, GoRouterState state) {
return MarkdownPage(docKey: key);
}
}
15 changes: 15 additions & 0 deletions apps/website/lib/router/routes/markdown_shell_route.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
part of 'package:website/router/router.dart';

@TypedShellRoute<MarkdownShellRoute>(
routes: [
TypedGoRoute<MarkdownPageRoute>(path: MarkdownPageRoute.path),
],
)
class MarkdownShellRoute extends ShellRouteData {
const MarkdownShellRoute();

@override
Widget builder(BuildContext context, GoRouterState state, Widget navigator) {
return DefaultLayout(child: navigator);
}
}
43 changes: 43 additions & 0 deletions apps/website/lib/ui/default_layout.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:website/hooks/use_markdown_doc_keys.dart';
import 'package:website/router/router.dart';

class DefaultLayout extends HookWidget {
const DefaultLayout({
required Widget child,
super.key,
}) : _navigator = child;

final Widget _navigator;

@override
Widget build(BuildContext context) {
final docKeys = useMarkdownDocKeys();

return Scaffold(
body: Row(
children: [
SizedBox(
width: 200,
child: ListView(
children: [
...docKeys.map(
(key) => ListTile(
title: Text(key),
onTap: () {
MarkdownPageRoute(key: key).go(context);
},
),
),
],
),
),
Expanded(
child: _navigator,
),
],
),
);
}
}
39 changes: 39 additions & 0 deletions apps/website/lib/ui/markdown_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// TODO: web only のlintを止める
import 'dart:js' as js;

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:website/hooks/use_markdown_doc.dart';

class MarkdownPage extends HookWidget {
const MarkdownPage({
required String? docKey,
super.key,
}) : _docKey = docKey;

final String? _docKey;

@override
Widget build(BuildContext context) {
final doc = useMarkdownDoc(_docKey);

if (doc == null) {
return const SizedBox.shrink();
}

return Markdown(
data: doc,
selectable: true,
onTapLink: (text, href, title) {
if (href == null) {
return;
}

// TODO: URLをみて、en/jpの切り替え対応

js.context.callMethod('open', [href]);
},
);
}
}
9 changes: 9 additions & 0 deletions apps/website/lib/utils/doc_key.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const _prefix = 'assets/docs/';

String toDocKey(String assetKey) {
return assetKey.substring(_prefix.length);
}

String toAssetKey(String docKey) {
return '$_prefix$docKey';
}
Loading

0 comments on commit 6fd2e8c

Please sign in to comment.