Flutter, React Native(Expo), Xamarin.Formsで、振る舞いが同じアプリを作っているんですが、その時にそれぞれのフレームワークで、Modalの実装はどうなるのか調べました。今回はボタンを押したらModalが表示する実装にしました。
調べた時に「要点となる実装部分だけの記載」という記事が多くて、初心者にとっては実装する時に「このコードはどこに追加(置き換え)するんだろうか?」って思ったので、FlutterとReact Native(expo)はコピペで動作するように記載しました。Xamarin.Formsはコピペの後でnamespaceを書き換えてください。
留意事項
その1
「こう実装すればこう動く、ってことが分かればそれでOK!」を方針に実装を進めた個人開発の内容を記載しましたので、「なぜその実装が必要なのか?」や「なぜその実装で動くのか?」は分かってない部分多数です。。。
その2
筆者は実戦経験が乏しい(というより、アプリ開発は個人開発を除いて、現場での開発が未経験。。)、なので、ご指摘は「ここは、こうした方が良いよ」くらいのノリでお願いします。
その3
「HelloWorldはできたけど、他にはこんなことできたら嬉しいな〜」って思っていた時のことを思い出して書きました。そのため、強々エンジニアや超人エンジニアの方々はこのページを参考にしない方が良いと思います。
※iOSとAndroidのバージョン
![Android-29(API Level)](https://img.shields.io/badge/Android-29(API Level)-brightgreen)
Flutter
以下の記事を参考に、FlutterでModal(Dialog)の実装を行いました。
参照記事:https://stackoverflow.com/questions/60047676/flutter-display-photo-or-image-as-modal-popup
概要
ボタンを押したらshowDialogを呼んで、showDialogからModalを呼び出します。(これだけです!)
実装
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> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Image Popup example')
),
backgroundColor: Colors.grey[800],
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(
onPressed: () async {
await showDialog(
context: context,
builder: (_) => ImageDialog()
);
},
color: Colors.blue,
child: Text(
'Open Modal',
style: TextStyle(
color:Colors.white,
fontSize: 20.0
),
),
),
],
),
)
);
}
}
class ImageDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Dialog(
child: Image.network(
'https://picsum.photos/250?image=9',
width: 500,
height: 500,
),
);
}
}
React Native(Expo)
以下の記事を参考に、React Native(Expo)でModal(Dialog)の実装を行いました。
参照記事:https://reactnative.dev/docs/modal
概要
React Nativeで用意されているModalを利用するんですが、Modalを閉じる場合にModalの枠外をタップしてもModalが閉じないため、Modalに閉じるボタンを実装しました。
実装
![React Native cli-2.0.1](https://img.shields.io/badge/React Native cli-2.0.1-brightgreen)
import React, { useState } from "react";
import { Modal, StyleSheet, Text, TouchableHighlight, View, Image } from "react-native";
export default function App() {
const [modalVisible, setModalVisible] = useState(false);
return (
<View style={styles.centeredView}>
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
>
<View style={styles.centeredView}>
<View style={styles.modalView}>
<Image style={styles.image}
source={{uri: 'https://picsum.photos/250?image=9'}}
/>
<TouchableHighlight
style={{ ...styles.openButton, backgroundColor: "#2196F3" }}
onPress={() => {
setModalVisible(!modalVisible);
}}
>
<Text style={styles.textStyle}>Hide Modal</Text>
</TouchableHighlight>
</View>
</View>
</Modal>
<TouchableHighlight
style={styles.openButton}
onPress={() => {
setModalVisible(true);
}}
>
<Text style={styles.textStyle}>Open Modal</Text>
</TouchableHighlight>
</View>
);
}
const styles = StyleSheet.create({
centeredView: {
flex: 1,
justifyContent: "center",
alignItems: "center",
marginTop: 22
},
modalView: {
width: 300,
height: 200,
margin: 20,
backgroundColor: "white",
borderRadius: 20,
padding: 35,
alignItems: "center",
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 2
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 5
},
openButton: {
backgroundColor: "#F194FF",
padding: 10,
elevation: 2
},
textStyle: {
color: "white",
fontWeight: "bold",
textAlign: "center"
},
modalText: {
marginBottom: 15,
textAlign: "center"
},
image: {
width: 400,
height: 300,
marginLeft: 10,
marginTop: 10,
}
});
Xamarin.Forms
以下の記事を参考に、Xamarin.FormsでModal(Dialog)の実装を行いました。
参照記事:https://docs.microsoft.com/ja-jp/xamarin/xamarin-forms/app-fundamentals/navigation/modal
概要
ボタンを押した時に別Class(ModalPage.xaml, ModalPage.xaml.cs)としてModalを呼び出す方法で実装しました。また、Modalを閉じる場合にModalの枠外をタップしてもModalが閉じないため、Modalに閉じるボタンを実装しました。
実装
<?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.ModalPage">
<StackLayout x:Name="layout">
<Image Source="https://picsum.photos/250?image=9" />
<Button x:Name="closeButton" Command="{Binding PopupCommand}" Text="Close Modal" Clicked="OnClicked" />
</StackLayout>
</ContentPage>
using System;
using Xamarin.Forms;
namespace xamarin.forms_project
{
public partial class ModalPage : ContentPage
{
public ModalPage()
{
InitializeComponent();
}
async void OnClicked(object sender, EventArgs e)
{
await Navigation.PopModalAsync();
}
}
}
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>
<Button x:Name="button" Command="{Binding PopupCommand}" Text="Open Modal" Clicked="OnSelected" />
</StackLayout>
</ContentPage>
using System;
using Xamarin.Forms;
namespace xamarin.forms_project
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
async void OnSelected(object sender, EventArgs e)
{
await Navigation.PushModalAsync(new ModalPage());
}
}
}
気になったこと
Xamarin.Formsで「Rg.Plugins.Popup」を入れた時にModal + Imageでは動作するんですが、modalにscrollviewやpinch-in/outを実装するとなぜか動作しませんでした。。
(動作させる方法をご存知の方、教えてくださいっっっっ!!)