フリーランス 技術調査ブログ

フリーランス/エンジニア Ruby Python Nodejs Vuejs React Dockerなどの調査技術調査の備忘録

Flutterでsqlliteを利用してタスク管理を実装する

はじめに

  • 作成途中のプログラムになりますが、Flutterでsqlliteを利用してタスクの登録と削除を行えるアプリを開発する。 ※変更処理は途中の状態です。

インストールのパッケージ

  • sqfliteとpath_providerをインストールする
dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  sqflite: ^2.0.2
  path_provider: ^2.0.8

コード

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';

import 'next_page.dart';


void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

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

class _MyHomePageState extends State<MyHomePage> {
  final textController = TextEditingController();
  int? selectedId;
  int tabIndex = 1;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home:Scaffold(
          appBar: AppBar(
              title: Text("タスク管理")
          ),
          body: Center(
            child: FutureBuilder<List<Task>>(
                future: DatabaseHelper.instance.getTasks(),
                builder: (BuildContext context,
                    AsyncSnapshot<List<Task>> snapshot) {
                  if (!snapshot.hasData) {
                    return Center(child: Text('Loading...'));
                  }
                  return snapshot.data!.isEmpty
                      ? Center(child: Text('No Tasks.'))
                      : ListView(
                    children: snapshot.data!.map((task) {
                      return Center(
                          child: Card(
                            color: selectedId == task.id
                                ? Colors.white70
                                : Colors.white,
                            child: ListTile(
                              title: Text(task.name),
                              onTap: () {
                                if (selectedId == null) {
                                  textController.text = task.name;
                                  selectedId = task.id;
                                } else {
                                  textController.text = '';
                                  selectedId = null;
                                }
                              },
                              onLongPress: () {
                                setState(() {
                                  DatabaseHelper.instance.remove(task.id!);
                                });
                              },
                            ),
                          )
                      );
                    }).toList(),
                  );
                }),
          ),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.save),
            onPressed: ()async {
              selectedId != null
                  ? await DatabaseHelper.instance.update(
                Task(id: selectedId, name: textController.text),
              )
                  : await DatabaseHelper.instance.add(
                Task(name: textController.text),
              );
              setState(() {
                textController.clear();
                selectedId = null;
              });
            },
          ),
          bottomNavigationBar: BottomNavigationBar(
            currentIndex: tabIndex,
            items: [
              BottomNavigationBarItem(
                label: 'Home',
                icon: Icon(Icons.home),
              ),
              BottomNavigationBarItem(
                label: 'AC Unit',
                icon: Icon(Icons.ac_unit),
              ),
            ],
            onTap: (int index) {
                setState((){
                  tabIndex = index;
                });
                showDialog(
                  context: context,
                  builder: (_) {
                    return AlertDialog(
                      title: Text("タイトル"),
                      content: TextField(
                        controller: textController,
                      ),
                      actions: <Widget>[
                        // ボタン領域
                        ElevatedButton(
                          child: Text("Cancel"),
                          onPressed: () => Navigator.pop(context),
                        ),
                        ElevatedButton(
                          child: Text("OK"),
                          onPressed: () async {
                            selectedId != null
                                ? await DatabaseHelper.instance.update(
                              Task(id: selectedId, name: textController.text),
                            )
                                : await DatabaseHelper.instance.add(
                              Task(name: textController.text),
                            );
                            setState(() {
                              textController.clear();
                              selectedId = null;
                            });
                            //Navigator.push(context, MaterialPageRoute(builder: (context) => NextPage()))
                          },
                        ),
                      ],
                    );
                  },
                );
                //Navigator.push(context, MaterialPageRoute(builder: (context) => NextPage()));
            },
          ),
        )
    );
  }
}

class Task {
  final int? id;
  final String name;

  Task({this.id, required this.name});

  factory Task.fromMap(Map<String, dynamic>json) => new Task(
      id: json['id'],
      name: json['name']
  );

  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'name': name
    };
  }
}

class DatabaseHelper {
  DatabaseHelper._privateConstructor();
  static final DatabaseHelper instance = DatabaseHelper._privateConstructor();

  static Database? _database;
  Future<Database> get database async => _database ??= await _initDatabase();

  Future<Database> _initDatabase() async {
    Directory documentsDirectory = await getApplicationDocumentsDirectory();
    String path = join(documentsDirectory.path, 'tasks.db');
    return await openDatabase(
      path,
      version: 1,
      onCreate: _onCreate,
    );
  }

  Future _onCreate(Database db, int version) async {
    await db.execute('''
      CREATE TABLE tasks(
          id INTEGER PRIMARY KEY,
          name TEXT
      )
      ''');
  }

  Future<List<Task>> getTasks() async {
    Database db = await instance.database;
    var tasks = await db.query('tasks', orderBy: 'name');
    List<Task> taskList = tasks.isNotEmpty
        ? tasks.map((c) => Task.fromMap(c)).toList()
        : [];
    return taskList;
  }
  Future<int> update(Task task) async {
    Database db = await instance.database;
    return await db.update('tasks', task.toMap(),
        where: "id = ?", whereArgs: [task.id]);
  }

  Future<int> add(Task task) async {
    Database db = await instance.database;
    return await db.insert('tasks', task.toMap());
  }

  Future<int> remove(int id) async {
    Database db = await instance.database;
    return await db.delete('tasks', where: 'id = ?', whereArgs: [id]);
  }
}

作成した画面

f:id:PX-WING:20220121201314p:plain

flutterでtimerを利用して自動で画面画面遷移する

サンプルコード

import 'dart:async';

import 'package:flutter/material.dart';

const Color darkBlue = Color.fromARGB(255, 18, 32, 47);

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: darkBlue,
      ),
      debugShowCheckedModeBanner: false,
      home: const Scaffold(
        body: Center(
          child: FirstScreen(),
        ),
      ),
    );
  }
}

/// Releaxing screen that stays visible for 3 seconds
class FirstScreen extends StatefulWidget {
  const FirstScreen({Key? key}) : super(key: key);

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

class _FirstScreenState extends State<FirstScreen> {
  //Declare a timer
  Timer? timer;


  @override
  void initState() {
    super.initState();

    timer = Timer(
      const Duration(seconds: 3),
      () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => const NextScreen(),
          ),
        );
      },
    );
  }

  @override
  void dispose() {
    super.dispose();
    timer?.cancel();
  }

  @override
  Widget build(BuildContext context) {
    return const Image(
      image: NetworkImage('https://flutter.github.io/assets-for-api-docs/assets/widgets/owl.jpg'),
    );
  }
}

class NextScreen extends StatelessWidget {
  const NextScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: Text("Next Screen"),
      ),
    );
  }
}

blog.logrocket.com

python/BeautifulSoupを利用して複数HTMLファイルを一括置換する

はじめに

  • 直近で某サイトのヘッダーやサイドナビが各ページに記述されており、ヘッダーやサイドナビが変更が発生するたびに対象ページをgrep 置換している運用しているサイトがあり、ヘッダーやサイドナビを共通化してほしいというお題を頂いたので、手作業で修正するよりプログラムで一括できないか調査してみた。

HTMLタグを置換するプログラム (その①)

  • BeautifulSoupを利用して置換対象のタグを取得し、取得した箇所の文字列を置換処理で対応をしようとしたところ、BeautifulSoupでHTMLを読み込むとパーサーがHTMLのフォーマットを勝手に置換してしまい、修正する必要がない箇所も修正してしまっていました。 またPHPソースコードエスケープしていまい、今回の作業には向かなかったので、違う方法を検討する。
import os
from html.parser import HTMLParser
from bs4 import BeautifulSoup

ROOT_PATH = '/code'
def find_all_files(directory):
    for cur_dir, dirs, files in os.walk(directory):
        for file in files:
            yield os.path.join(cur_dir, file)

## 共通ヘッダー変換
def header_change(): 
    for file in find_all_files(ROOT_PATH):
        if ('.htm' in file or '.php' in file):
            f = open(file, 'r+', encoding='UTF-8', errors='ignore')
            data = f.read()
            soup = BeautifulSoup(data, 'html.parser')
            new_tag = soup.new_tag('b')
            new_tag.string = "一時的に挿入する文字列です。その後置換する文字列に切り替わります。"
            nav = soup.find("nav")
            if nav is None:
                print(f'ヘッダーがないページ,{file}')
            else:                 
                soup.nav.insert_before(new_tag)
                soup.nav.extract()
                f.seek(0)
                new_html = soup.prettify(formatter=None)
                if ('en/' in file):
                    print(f'英語ページ,{file}')
                    f.write(new_html.replace('<b>一時的に挿入する文字列です。その後置換する文字列に切り替わります。</b>', "<?php include($_SERVER['DOCUMENT_ROOT'].'/common/html/global_navi_en.html'); ?>"))
                    f.write(data.replace(str(nav), "<?php include($_SERVER['DOCUMENT_ROOT'].'/common/html/global_navi_en.html'); ?>"))
                else:
                    print(f'日本語ページ,{file}')
                    f.write(new_html.replace('<b>一時的に挿入する文字列です。その後置換する文字列に切り替わります。</b>', "<?php include($_SERVER['DOCUMENT_ROOT'].'/common/html/global_navi.html'); ?>"))
                f.truncate()
                f.close()    
            f.close()

header_change()

HTMLタグを置換するプログラム (その②)

  • 下記のプログラムは対象ディレクトにあるHTMLファイルおよびPHPファイルの中に記述されている特定のタグの開始位置と終了位置を取得し、取得した位置の範囲あるタグを共通ヘッダーを読み込む処理のコードに置換する処理となっております。 下記の記述にすることで特に問題なく置換することができました。
import os

ROOT_PATH = '/code'

def find_all_files(directory):
    for cur_dir, dirs, files in os.walk(directory):
        for file in files:
            yield os.path.join(cur_dir, file)


## 共通ヘッダー変換
def header_change(): 
    for file in find_all_files(ROOT_PATH):
        if ('.htm' in file or '.php' in file):
            f = open(file, 'r+', encoding='UTF-8', errors='ignore')
            data = f.read()
            start = data.find('<nav id="globalNav">', 0)
            end = data.find('</nav>', start)
            if start == -1:
                print(f'ヘッダーがないページ,{file}')
            else:
                if ('common/html' not in file):
                    f.seek(0)
                    if ('en/' in file):
                        print(f'Englishページ,{file}')
                        f.write(data.replace(data[start:end+6], "<?php include($_SERVER['DOCUMENT_ROOT'].'/hoge_navi_en.html'); ?>"))
                    else:
                        print(f'日本語ページ,{file}')
                        f.write(data.replace(data[start:end+6], "<?php include($_SERVER['DOCUMENT_ROOT'].'/hoge_navi.html'); ?>"))
                    f.truncate()
            f.close()

header_change()

上記の件を動かしていた環境

docker-compose.yml

version: '3'
services:
  python:
    build: .
    volumes:
      - ../htdocs:/code
    tty: true

Dockerfile

FROM python:3.10-alpine
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt

requirements.txt

beautifulsoup4

FlutterとFirebaseの連携手順のメモ

FirebaseとFlutterの設定

  • Firebaseにプロジェクトを作成する

  • google-service.jsonファイルをダウンロードして下記にフォルダに設置する

\<Your Project Root>\android\app\google-services.json
  • /<Your Project Root>/android/build.gradleファイルにclasspath 'com.google.gms:google-services:4.3.10'に追加する
    dependencies {
        classpath 'com.android.tools.build:gradle:4.1.0'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'com.google.gms:google-services:4.3.10'
    }
  • android/app/build.gradleファイルに下記の記述を追記する
apply plugin: 'com.google.gms.google-services'


dependencies {
    ・・・・
    implementation platform('com.google.firebase:firebase-bom:29.0.3')
    implementation 'com.google.firebase:firebase-analytics-ktx'
}

エラーが発生したときの対処

  • 上記の設定をしてもエラーが発生する場合、下記の対応して解決したので備忘録としてメモをしておく。

  • android/app/build.gradleファイルの下記の箇所を修正する

From

        minSdkVersion flutter.minSdkVersion
        targetSdkVersion flutter.targetSdkVersion

To

   minSdkVersion 19
   targetSdkVersion 30
  • android/build.gradleファイルは下記のように変更する From
    //ext.kotlin_version = '1.3.50'
    ext.kotlin_version = '1.6.10'

Flutterのパッケージインストール

  • pubspec.yamlファイルにcloud_firestorefirebase_coreの記述を追加する
dependencies:
  flutter:
   sdk: flutter
(省略)
   cloud_firestore: ^3.1.5
   firebase_core: ^1.10.6
  • android/app/build.gradleファイルにmultiDexEnabledの記述を追加する
    defaultConfig {
(省略)
        multiDexEnabled true
    }

Flutterの実装

  • lib/main.dartファイルのmain関数にFirebaseの初期化とWidgetsFlutterBinding.ensureInitializedを追加する
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(const MyApp());
}
  • 参考URL

stackoverflow.com

Firestoreへの登録処理

  • 下記のクラスを呼び出すとFirebasestoreにデータが書き込まれる。
import 'package:cloud_firestore/cloud_firestore.dart';

class Firestore {
  static FirebaseFirestore _firebaseFirestore = FirebaseFirestore.instance;
  
  static Future<void> addUser() async {
    try{
      final userRef = await _firebaseFirestore.collection('user').add({
        'name': '山田 太郎',
        'imagePath': 'https://item-shopping.c.yimg.jp/i/l/project1-6_4530956470801',
      });
      print('アカウント作成に成功しました。$userRef');
    } catch (e){
      print('アカウント作成に失敗しました  $e');
    }
  }
}

f:id:PX-WING:20211231193043p:plain

Nuxtjs+ExpressでPDF作成及びダウンロード

はじめに

  • Nuxtjs+Expressで構築したシステムでP DFの作成及びダウンロードする機能の実装をする必要があり、技術的に調査した結果を下記にまとめました。

NodejsでPDFを生成できるライブラリ

  • NodejsでPDFを生成できるライブラリをいくつかピックアップしてみて、スター数が多い「pdfmake」を利用してPDFを生成してみることにします。個人的にはレイアウトを簡単に作成できる、node-html-pdfが好きです。※使ったことはありませんが。
- pdfmake [STAR数: 9.8k]
https://github.com/bpampuch/pdfmake

- pdfkit [STAR数: 7.7k]
https://github.com/foliojs/pdfkit

- node-html-pdf [STAR数: 3.4k]
https://github.com/marcbachmann/node-html-pdf/releases

パッケージのインストール

  • 下記のパッケージをインストールする。
yarn add pdfmake --save
yarn add pdfmake-unicode --save
yarn add @types/pdfmake --save-dev

日本語対応するためにフリーフォントをダウンロードする

  • 下記のサイトからフリーフォントをダウンロードして、ダウンロードしたフォントファイルを「assets」フォルダにファイルを格納する jikasei.me

f:id:PX-WING:20211228235836p:plain

Express(バックエンド側の処理)

  • API側でPDFを作成する。
import PdfPrinter from "pdfmake";
import fs from "fs";

export async function postCreatePdf(req: Request, res: Response) {
  const params = req.body

  // PDFファイルの生成
  createPDF(params).then(function(value) {

    // 5秒後にファイルをダウンロードする処理をする。
    // 理由はPDFファイルを生成して直後にダウンロードすると、ファイルが壊れた状態でダウンロードされてしまうため、
 // 少し時間をおいてからダウンロードする様にする
    setTimeout(async function() {

      // ブラウザ表示
      //var data = fs.readFileSync("/<YourAppRoot>/sample.pdf");
      //res.contentType("application/pdf");
      //res.send(data);

      // ファイルダウンロード
      res.download("/<YourAppRoot>/sample.pdf");

    }, 5000);
  });
}

// PDFファイル生成処理
async function createPDF(searchAccoutIdParams: any) {
  return new Promise(async function(resolve, reject) {
    try {

      // アセットフォルダに設置したフォントサイズを読み込む
      const fonts = {
        GenShinGothic: {
          normal: '/<YourAppRoot>/assets/fonts/GenShinGothic/GenShinGothic-Normal.ttf',
          bold: '/<YourAppRoot>/assets/fonts/GenShinGothic/GenShinGothic-Normal.ttf',
        },
      };
      
      const PdfPrinter = require('pdfmake');
      const printer = new PdfPrinter(fonts);
      const fs = require('fs');

      // PDFファイルのレイアウトを指定する
      const docDefinition = {
        content: [
          { text: 'こちらはサンプルです。', style: 'title' },
          {
            table: {
              headerRows: 1,
              body: [
                ['Header 1', 'Header 2', 'Header 3'],
                ['Sample value 1', 'Sample value 2', 'Sample value 3'],
                ['Sample value 1', 'Sample value 2', 'Sample value 3'],
                ['Sample value 1', 'Sample value 2', 'Sample value 3'],
                ['Sample value 1', 'Sample value 2', 'Sample value 3'],
                ['Sample value 1', 'Sample value 2', 'Sample value 3'],
              ]
            },
            layout: {
              hLineStyle: function (i: any, node: any) {
                if (i === 0 || i === node.table.body.length) {
                  return null;
                }
                return {dash: {length: 10, space: 4}};
              },
              vLineStyle: function (i: any, node: any) {
                if (i === 0 || i === node.table.widths.length) {
                  return null;
                }
                return {dash: {length: 4}};
              },
            }
          },      
          {
          layout: 'lightHorizontalLines', // optional
          table: {
            // headers are automatically repeated if the table spans over multiple pages
            // you can declare how many rows should be treated as headers
            headerRows: 1,
            widths: [ '*', 'auto', 100, '*' ],
    
            body: [
              [ 'First', 'Second', 'Third', 'The last one' ],
              [ 'Value 1', 'Value 2', 'Value 3', 'Value 4' ],
              [ { text: 'Bold value', bold: true }, 'Val 2', 'Val 3', 'Val 4' ]
            ]
          }}    
        ],
        styles: {
          h1: {
            font: 'GenShinGothic',
            fontSize: 18,
            bold: true
          },
          style2: {
            alignment: 'right',
            color: 'blue',
          }
        },
        defaultStyle: {
          font: 'GenShinGothic',
          fontSize: 14,
        }
      };

      // PDFファイルの作成
      // <YourAPPRoot>/sample.pdfファイルが作成される
      const pdfDoc = printer.createPdfKitDocument(docDefinition);
      pdfDoc.pipe(fs.createWriteStream('sample.pdf'));
      pdfDoc.end();  
      resolve(true)
    } catch (e) {
      reject(false)
    }
  });
}

フロント側(Nuxtjts)

  • axiosでExpressのAPIを呼び出す
    async createPdf() {
      // responseTypeを指定しないとうまくPDFファイルをダウンロードできません。
      // createメソッドで一度、responseTypeを指定したインスタンスを作成する
      const axios = this.$axios.create({
        'responseType': 'blob',
      });
 
      axios.post('/admin/create-pdf', { userId: 100}).then(response => {
          const blob = new Blob([response.data], { type: "application/pdf" });
          const url = (window.URL || window.webkitURL).createObjectURL(blob);
          const a = document.createElement("a");
          a.href = url;
          a.download = "test.pdf";
          document.body.appendChild(a);
          a.click();
          document.body.removeChild(a);
      })
    },

実行結果

  • 上記の処理を実行した時に生成されたPDFファイルのサンプルは下記のようになります。 f:id:PX-WING:20211229001217p:plain

今回参考にさせて頂いた記事

https://stackoverflow.com/questions/52817280/problem-downloading-a-pdf-blob-in-javascript
https://blog.kozakana.net/2018/04/express-csv-download/
https://nodejs.keicode.com/nodejs/how-to-create-pdf-file.php
https://hapicode.com/javascript/pdfmake.html#npm-%E3%81%8B%E3%82%89%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B%E5%A0%B4%E5%90%88
https://stackoverflow.com/questions/31105846/how-to-send-a-pdf-file-from-node-express-app-to-the-browser
https://qiita.com/akamushi/items/661397c297c2e83acc73
https://github.com/bpampuch/pdfmake/issues/724

問題点

  • PDFファイルがちゃんと生成されるまで5秒で問題ないのか、複雑なPDFファイルを生成したときに5秒以上、待ってからダウンロードしないといけないのか検証が必要である。
  • pdfmakeでどこまで複雑なレイアウトが実現可能なのか調査が必要
  • pdfmakeで作成したPDFがサーバーに残ってしまうので、削除するタイミングをどうするか検証する必要がある

flutterのスプラッシュ画面

インストール

flutter pub add flutter_native_splash
  • pubspec.yamlに次の設定を追加する。画像はpngを指定する必要がある。
flutter_native_splash:
  image: "images/sample.png"
  color: "FAF3F3"

スプラッシュ画面の作成

flutter pub run flutter_native_splash:create

スプラッシュの設定を変更する場合

flutter clean
flutter pub get
flutter pub run flutter_native_splash:create

実機で検証する方法(Android

  • プロジェクトのrootディレクトリで下記のコマンドを実行する
flutter build apk
  • プロジェクトの下記のフォルダにapp-release.apkファイルがあるので、そちらをメールなどで検証端末に送信しテストする。
 build/app/outputs/apk/release/app-release.apk

flutterで画面遷移とパラメータの受け渡しをする

はじめに

  • 久しぶりにflutterの勉強を再開してみる。

flutter doctorでエラーの解消

  • 久しぶりに動かそうとしたら、flutter doctorでエラーになった
C:\Users\user>flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 2.5.3, on Microsoft Windows [Version 10.0.19043.1415], locale ja-JP)
[!] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    X cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
      See https://developer.android.com/studio/command-line for more details.
    X Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/windows#android-setup for more details.
[√] Chrome - develop for the web
[√] Android Studio (version 2020.3)
[√] VS Code (version 1.62.3)
[√] Connected device (2 available)

! Doctor found issues in 1 category.

cmdline-tools component is missingエラー

  • 下記の赤枠の箇所にチェックを入れて Applyボタンをクリックすることで解消できました。 f:id:PX-WING:20211223084412p:plain

Android license status unknown.エラー

  • 下記のコマンドを実行するとことでエラーを解消することが出来ました。
 flutter doctor --android-licenses

画面遷移するサンプル

  • トップ画面
import 'dart:ui';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:winlogic/next_page.dart';

// よいサンプル
// https://github.com/kenta-wakasa/riverpod_sample

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'トップページ'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

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

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

  void _changeText(input) {
    print(input);
    setState(() {
      _text = input;
    });
  }

  final ButtonStyle style =
  ElevatedButton.styleFrom(textStyle: const TextStyle(fontSize: 20));

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
        actions: <Widget>[Icon(Icons.add), Icon(Icons.share)],
      ),
      body: Container(
          width: double.infinity,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              //Image.network('https://flutter.github.io/assets-for-api-docs/assets/widgets/owl.jpg'),
              Image.asset('images/sample.jpg', height: 200),
              Icon(Icons.holiday_village,size:30),
              Text(_text),
              Text("テストテスト",
                style: TextStyle(
                  fontSize: 40,
                  color: Colors.green,
                  fontWeight: FontWeight.w900,
                  fontStyle: FontStyle.italic,
                )
              ),
              Text("サンプルテキスト"),
              ElevatedButton(
                  child: Text("次へ"),
                  onPressed: () async {
                    final result = await Navigator.push(
                        context,
                        MaterialPageRoute(builder: (context) => NextPage('1ページ目から渡すパラメータ'))
                    );
                    _text = result;
                    _changeText(result);
                    print(_text);
                  }
              )
            ],
          )
      ),
      floatingActionButton: FloatingActionButton(
      onPressed: _incrementCounter,
      tooltip: 'Increment',
      child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
  );
  }
}
  • 2画面目
import 'package:flutter/material.dart';

class NextPage extends StatelessWidget {
  NextPage(this.name);
  final String name;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('2ページ目'),
        actions: <Widget>[Icon(Icons.add), Icon(Icons.share)],
      ),
      body: Container(
        height: double.infinity,
        color: Colors.greenAccent,
        child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
            Text(name),
            Center(
              child: ElevatedButton(
                  child: Text('戻る'),
                  onPressed: () {
                    Navigator.pop(context, "2ページ目から渡すパラメータ");
                  }
              )
            ),
          ]
        )
      )
    );
  }
}
  • pubspec.yamlに下記の記述をしないとassetで画像を表示することが出来ない
flutter:
  assets:
    - images/sample.jpg