Flutter, React Native(Expo), Xamarin.Formsでテキストの表示を別のファイルに切り出す場合の実装を比べる

Flutter, React Native(Expo), Xamarin.Formsで、振る舞いが同じアプリを作っているんですが、その時にそれぞれのフレームワークでテキスト表示の部分だけを、別のファイルに切り出した(ファイル分割した)場合にどのようになるのか調べました。
1つのファイルで作り込んでそのファイルが肥大化した場合に、どうやってファイルを切り出すか知っていると、プロジェクトの内部構成を整理整頓する時に役立つと思い。。。

# 留意事項
## その１
「こう実装すればこう動く、ってことが分かればそれでOK！」を方針に実装進めた個人開発の内容を記載しましたので、「なぜその実装が必要なのか？」や「なぜその実装で動くのか？」は分かってない部分多数です。。。 :bow:

## その２
筆者は実戦経験が乏しい（というより、アプリ開発は個人開発を除いて、現場での開発が未経験。。）、なので、ご指摘は「ここは、こうした方が良いよ:wink:」くらいのノリでお願いします。:pray::pray::pray:

## その３
「HelloWorldまでできたけど、こんなことできたら嬉しいな〜」って自分が思っていた時のことを思い出して書きました。そのため、強々エンジニアや超人エンジニアの方々はこのページを参考にしない方が良いと思います。:expressionless::sleepy: :cold_sweat:

# Flutter

以下の記事を参考に、Flutterでテキストの表示を別ファイルに切り出しました。
参照記事：https://qiita.com/i47_rozary/items/21ea4ba1713334b2a4ba

## 切り出し前

```dart:main.dart
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text("切り出す部分のテキスト表示"), //このテキストを別ファイルに切り出す
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

```

## 切り出し後

別ファイルに切り出す場合、大まかには以下のことをやります

1. 「child.dart」ファイルを新規作成
1. child.dartファイルにmain.dartから呼び出すときのクラス名「Child」を作成
    1. オプションでmain.dartから呼び出すメソッド「callChild」を生成
1. child.dartにTextを生成
1. main.dartにchild.dartをimport
1. main.dartのText部分をchild.dartのクラス名「Child」に変更

```dart:child.dart
import 'package:flutter/material.dart';

GlobalKey<_ChildState> childKey = new GlobalKey<_ChildState>();

//呼び出すクラス名を「Child」に統一
class Child extends StatefulWidget {
  Child({Key key}) : super(key: key);

  @override
  _ChildState createState() => _ChildState();
}

//Stateにするクラス名を「Child」に統一
class _ChildState extends State<Child> {

  //main.dart(呼び出し元)からChildクラスのメソッドを呼べるようにするため、メソッドを作成
  void callChild(String message) {
    print(message);
  }

  @override
  Widget build(BuildContext context) {
    return new Text('切り出す部分のテキスト表示');
  }
}
```
```dart:main.dart
import 'package:flutter/material.dart';
import 'child.dart'; //切り出したテキストを表示するファイルをimport

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    // 切り出したChildクラスにあるcallChildメソッドを呼び出す
    // Childクラスに設定したkey「childKey」を使って、callChildメソッドを呼び出す
    childKey.currentState.callChild("message");
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            //呼び出すクラス「Child」
            //Childクラスのメソッドを呼ぶ場合のkey「childKey」を設定
            Child(key: childKey),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

```

# React Native(Expo)
以下の記事を参考に、React Native(Expo)でテキストの表示を別ファイルに切り出しました。
参照記事：https://dev.to/codekagei/react-native-custom-tab-component-reactnative-navigation-1d39


## 切り出し前

```
import React from 'react';
import { StyleSheet, View, Text } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <Text>切り出す部分のテキスト表示</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

```

## 切り出し後
別ファイルに切り出す場合、大まかには以下のことをやります

1. 「Child.js」ファイルを新規作成
1. Child.jsファイルにTextを生成
1. App.jsにChild.jsをimport
1. App.jsのText部分をChild.jsの宣言「Child」に変更


```javascript:Child.js
import React from "react";
import { Text, StyleSheet } from "react-native";

export default function App() {
  return (
    <Text style={styles.textWhite}>切り出す部分のテキスト表示</Text>
  );
};

const styles = StyleSheet.create({
  textWhite: {
    backgroundColor: "white",
    color: "black"
  }
});
```

```javascript:App.js
import React from 'react';
import { StyleSheet, View } from 'react-native';
//タグ「Child」で統一
import Child from "./Child";

export default function App() {
  return (
    <View style={styles.container}>
      //Childタグでchild.jsのTextを参照(?)する
      <Child />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});
```

# Xamarin.Forms
以下の記事を参考に、Xamarin.Formsでテキスト(Label)の表示を別ファイルに切り出しました。
参照記事：https://somostechies.com/using-xml-namespaces-to-optimize-your-imports-in-xamarin-forms/

## 切り出し前

```xml:MainPage.xaml
<?xml version="1.0" encoding="utf-8"?>
<ContentPage
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    x:Class="xamarin.forms_project.MainPage">
    <StackLayout>
        <Frame BackgroundColor="#2196F3" Padding="24" CornerRadius="0">
            <Label Text="Welcome to Xamarin.Forms!" HorizontalTextAlignment="Center" TextColor="White" FontSize="36" />
        </Frame>
        <Label FontSize="26"
             TextColor="Black"
             BackgroundColor="Aqua">切り出す部分のテキスト表示</Label>
    </StackLayout>
</ContentPage>
```

## 切り出し後

別ファイルに切り出す場合、大まかには以下のことをやります

1. 新しいファイル -> ContentPage XAML -> 「MyLabel」ファイルを新規作成
1. MyLabel.xamlファイルにLabelを生成
1. MainPage.xamlのnamespaceにMyLabel.xamlがある階層をmypageとして定義
1. MainPage.xamlのLabel部分をmypage:MaLabelに変更

```xml:MyLabel.xaml
<?xml version="1.0" encoding="UTF-8"?>
<Label xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             FontSize="26"
             TextColor="Black"
             BackgroundColor="Aqua"
             x:Class="xamarin.forms_project.Pages.MyLabel">
    切り出す部分のテキスト表示
</Label>
```

```xml:MainPage.xaml
<?xml version="1.0" encoding="utf-8"?>
<ContentPage
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:mypage="clr-namespace:xamarin.forms_project.Pages"
    x:Class="xamarin.forms_project.MainPage">
    <StackLayout>
        <Frame BackgroundColor="#2196F3" Padding="24" CornerRadius="0">
            <Label Text="Welcome to Xamarin.Forms!" HorizontalTextAlignment="Center" TextColor="White" FontSize="36" />
        </Frame>
        <mypage:MyLabel />
    </StackLayout>
</ContentPage>
```

# 感想
「別のファイル作って、そこに書くだけじゃん！」って、思ってた時の自分にこのページを教えてあげたい:smirk: