lesson-2_サインイン、ホーム画面の作成をしよう
📲 サインイン、ホーム画面を作成しよう
ではここからは実際のメニューを作成していきましょう!
まずサインイン画面を作成していきます。
lib/view/screens/signin.dart
へ移動して以下のコードに書 き換えていきましょう!
[signin.dart
]
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hexcolor/hexcolor.dart';
import 'package:provider/provider.dart';
import 'package:responsive_framework/responsive_framework.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart';
import '../../model/contract_model.dart';
import '../widgets/navbar.dart';
class SignIn extends StatelessWidget {
const SignIn({Key? key}) : super(key: key);
static Web3App? _walletConnect;
static String? _url;
static SessionData? _sessionData;
String get deepLinkUrl => 'metamask://wc?uri=$_url';
Future<void> _initWalletConnect() async {
_walletConnect = await Web3App.createInstance(
projectId: dotenv.env["WALLETCONNECT_PROJECT_ID"]!,
metadata: const PairingMetadata(
name: 'NEAR MulPay',
description: 'Mobile Payment dApp with Swap Feature',
url: 'https://walletconnect.com/',
icons: [
'https://walletconnect.com/walletconnect-logo.png',
],
),
);
}
Future<void> connectWallet() async {
if (_walletConnect == null) {
await _initWalletConnect();
}
try {
// セッション(dAppとMetamask間の接続)を開始します。
final ConnectResponse connectResponse = await _walletConnect!.connect(
requiredNamespaces: {
'eip155': const RequiredNamespace(
chains: ['eip155:1313161555'],
methods: ['eth_signTransaction', 'eth_sendTransaction'],
events: ['chainChanged']),
},
);
final Uri? uri = connectResponse.uri;
if (uri == null) {
throw Exception('Invalid URI');
}
final String encodedUri = Uri.encodeComponent('$uri');
_url = encodedUri;
// Metamaskを起動します。
await launchUrlString(deepLinkUrl, mode: LaunchMode.externalApplication);
// セッションが確立されるまで待機します。
final Completer<SessionData> session = connectResponse.session;
_sessionData = await session.future;
} catch (e) {
rethrow;
}
}
@override
Widget build(BuildContext context) {
final displayHeight = MediaQuery.of(context).size.height;
final displayWidth = MediaQuery.of(context).size.width;
final isDeskTop = ResponsiveBreakpoints.of(context).largerThan(MOBILE);
var provider = Provider.of<BottomNavigationBarProvider>(context);
return Scaffold(
body: SafeArea(
child: SizedBox.expand(
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
image: DecorationImage(
image: const AssetImage("assets/multiple-coins.jpg"),
alignment: Alignment(isDeskTop ? 0 : -0.3, 0.5),
fit: BoxFit.fitHeight,
colorFilter: ColorFilter.mode(
Colors.black.withOpacity(0.6),
BlendMode.dstATop,
),
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
height: displayHeight * 0.1,
),
ShaderMask(
blendMode: BlendMode.modulate,
shaderCallback: (size) => LinearGradient(
colors: [HexColor("#7AD6FE"), HexColor("#04494E")],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
).createShader(
Rect.fromLTWH(0, 0, size.width, size.height),
),
child: const Text(
"MulPay",
style: TextStyle(
color: Colors.white,
fontSize: 60.0,
fontWeight: FontWeight.bold,
),
),
),
SizedBox(
height: displayHeight * 0.03,
),
Container(
width: isDeskTop ? displayWidth * 0.4 : displayWidth,
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 15),
margin: const EdgeInsets.symmetric(horizontal: 20),
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(5),
),
child: const Text(
'You can make a payment\n with multiple kinds of coin',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 25),
),
),
SizedBox(
height: displayHeight * 0.5,
),
SizedBox(
height: displayHeight * 0.1,
width: isDeskTop ? displayWidth * 0.4 : displayWidth * 0.7,
child: ElevatedButton(
onPressed: () async {
try {
await connectWallet();
await context.read<ContractModel>().setConnection(
deepLinkUrl, _walletConnect!, _sessionData!);
provider.currentIndex = 0;
Navigator.pushReplacementNamed(context, '/home');
} catch (error) {
debugPrint('error $error');
}
},
child: Text(
'Connect Wallet',
style: GoogleFonts.patuaOne(
fontWeight: FontWeight.w500,
fontSize: 27,
color: Colors.black),
),
),
),
],
),
),
),
),
);
}
}
では次にmain.dart
へ移動して下のように変更しましょう。
[main.dart
]
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hexcolor/hexcolor.dart';
import 'package:provider/provider.dart';
import 'package:responsive_framework/responsive_framework.dart';
import 'model/contract_model.dart';
import 'view/screens/signin.dart';
import 'view/widgets/navbar.dart';
Future main() async {
await dotenv.load(fileName: ".env");
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider<BottomNavigationBarProvider>(
create: (BuildContext context) => BottomNavigationBarProvider(),
),
ChangeNotifierProvider(
create: (context) => ContractModel(),
),
],
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
builder: (context, child) => ResponsiveBreakpoints.builder(
child: child!,
breakpoints: [
const Breakpoint(start: 0, end: 450, name: MOBILE),
const Breakpoint(start: 451, end: double.infinity, name: DESKTOP),
],
),
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
primary: HexColor("#57A8BA"),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0),
),
shadowColor: Colors.black,
elevation: 10,
),
),
fontFamily: GoogleFonts.plusJakartaSans().fontFamily,
textTheme: TextTheme(
headlineSmall: TextStyle(
fontFamily: GoogleFonts.baloo2().fontFamily,
fontSize: 36,
height: 1.0,
color: Colors.black,
),
),
scaffoldBackgroundColor: HexColor('#C1E3F5'),
),
routes: {
'/signIn': (context) => SignIn(),
'/home': (context) => const BottomNavigationBarWidget(),
},
initialRoute: '/signIn',
);
}
}
flutterではmain
関数から走り出します。まず.env
ファイルを読み込み、その後JavaScriptでいうpropsのようなprovider
を作成します。
このproviderを使用することで他のウィジェットからも情報の共有、監視ができるようになります。
Future main() async {
await dotenv.load(fileName: ".env");
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider<BottomNavigationBarProvider>(
create: (BuildContext context) => BottomNavigationBarProvider(),
),
ChangeNotifierProvider(
create: (context) => ContractModel(),
),
],
child: const MyApp(),
),
);
}
childとしてMyApp
が指定されているので次にこのウィジェットが表示されることになります。
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
builder: (context, child) => ResponsiveBreakpoints.builder(
child: child!,
breakpoints: [
const Breakpoint(start: 0, end: 450, name: MOBILE),
const Breakpoint(start: 451, end: double.infinity, name: DESKTOP),
],
),
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
primary: HexColor("#57A8BA"),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0),
),
shadowColor: Colors.black,
elevation: 10,
),
),
fontFamily: GoogleFonts.plusJakartaSans().fontFamily,
textTheme: TextTheme(
headline1: TextStyle(
fontFamily: GoogleFonts.baloo2().fontFamily,
fontSize: 36,
height: 1.0,
color: Colors.black,
),
),
scaffoldBackgroundColor: HexColor('#C1E3F5'),
),
routes: {
'/signIn': (context) => SignIn(),
'/home': (context) => const BottomNavigationBarWidget(),
},
initialRoute: '/signIn',
);
}
}
このコードによって画面の横のサイズが450ピクセル以下のものをモバイル、それ以上をデスクトップとして指定しています。
builder: (context, child) => ResponsiveBreakpoints.builder(
child: child!,
breakpoints: [
const Breakpoint(start: 0, end: 450, name: MOBILE),
const Breakpoint(start: 451, end: double.infinity, name: DESKTOP),
],
),
MyApp
ではroutes
でルートを指定しています。ここでは/signIn
というルートではSignIn
ウィジェットが、/home
というルートではBottomNavigationBarWidget
ウィジェットが表示されることになります。
initialRoute
には/signIn
が指定されているのでまずはSignIn
ウィジェットが表示されます。
ではエミュレータで動かしてみましょう!
その前に、使用しているライブラリの中でandroidの設定を変えないと動かないものがあるのでandroid/app/build.gradle
に移動してdefaultConfig
の中のminSdkVersion
を20
にしましょう。
android {
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.client"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
minSdkVersion 20
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
ここまでで一度UIを確認してみましょう!
まずは環境構築で準備したエミュレータまたは実機を起動してPCと接続します。その後、下のコマンドを実行することでアプリを起動しましょう。
これ以降も、UIを確認する際は同じ手順で行います。
yarn client flutter:run