🧩

Serverpod 1.0.0覚え書き 3

2023/03/18に公開

大きな目標

個人開発のbackendとしてServerpodを活用できるようになる。

中期的な目標

Serverpodの操作手順に慣れる。

現状

新しいtutorialをハンズオン。今日の課題はこれ。
https://www.youtube.com/watch?v=JOgj-Vley6U&t=44s

  • terminalで serverpod create podpod

あら、できない。Dockerで前回のプロジェクトが走っていた。これを止めて再挑戦。できた。
画面を見ていると、severpodがまだ0.9.4だ。もしうまくいかなかったら、「見本が古かったせい」にしよう。もちろん、たいがいは私のミスだとわかっているが・・・。

前回の動画よりこちらのほうが、大量に自動生成されるfileの解説がちょっと詳しい。
VSCodeでpodpodの三つ(serverとclientとflutter)をまとめて開いているが、terminalでpodpod_serverに移動。

  • dart bin/main.dart

これでlocalhost:8080が開通

  • simulatorを立ち上げて走らせると、デモアプリが開く。

Flutterのカウンターアプリじゃなく、伝統的HalloWorldをちょっとアレンジしたDemoになっている。名前を入力すると、挨拶してくれる系。

今回はここまで来てやっとendpointに入る。新しいfileをつくって、とりあえずこんな感じ。

podpod_server/lib/src/endpoints/podpod_endpoint.dart

import 'package:serverpod/serverpod.dart';

class PodpodEndpoint extends Endpoint {
  Future<String> getPod(Session session, {String version = "1.0"}) async {
    return "Pod-Pod $version";
  }
}
  • terminal で serverpod generate

ここで、podpod_clientにgenerateされた lib/src/protocol/client.dartを見にいくのだけれど、ここの表記がちょっと現状と違っている。「古いからだ〜」と信じて放置。

version = "1.0"という設定にしたのは、ここからincrementしていくのかと思ったのだけれど、そうでもなくて、ただ単に「関数を書いてみた」というだけで、挙動ははhellor"name"の時と変わらなかった。

実は公式のほうの動画では、Flutter画面からの読み込みtimeoutになるという問題があって、その原因が私にはわからないし、直しようもなかったので、こちらの動画を試したのだけれど、こちらではtableを用意してDBを使ってみる、というというところまで行かなかったので、結局timeout問題は未解決のままだ。ざーんねん。

これってproviderとか、riverpodとか、どうなるんだろう。

ちょっと長いけど、flutterのmain.dartはこんな感じ。

import 'package:podpod_client/podpod_client.dart';
import 'package:flutter/material.dart';
import 'package:serverpod_flutter/serverpod_flutter.dart';

// Sets up a singleton client object that can be used to talk to the server from
// anywhere in our app. The client is generated from your server code.
// The client is set up to connect to a Serverpod running on a local server on
// the default port. You will need to modify this to connect to staging or
// production servers.
var client = Client('http://localhost:8080/')
  ..connectivityMonitor = FlutterConnectivityMonitor();

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

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Serverpod Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Pod-Pod Charm'),
    );
  }
}

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

  final String title;

  
  MyHomePageState createState() => MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  // These fields hold the last result or error message that we've received from
  // the server or null if no result exists yet.
  String? _resultMessage;
  String? _errorMessage;

  final _textEditingController = TextEditingController();
  
  void initState() {
    super.initState();
    getData();
  }

  getData() async {
    _resultMessage = await client.podpod.getPod(spell: "");
    setState(() {
      
    });
  }

  // Calls the `hello` method of the `example` endpoint. Will set either the
  // `_resultMessage` or `_errorMessage` field, depending on if the call
  // is successful.
  void _callCharm() async {
  //void _callHello() async {

    try {
      //final result = await client.example.hello(_textEditingController.text);
      final result =
          await client.podpod.getPod(spell: _textEditingController.text);

      setState(() {
        _resultMessage = result;
      });
    } catch (e) {
      setState(() {
        _errorMessage = '$e';
      });
    }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            Padding(
              padding: const EdgeInsets.only(bottom: 16.0),
              child: TextField(
                controller: _textEditingController,
                decoration: const InputDecoration(
                  hintText: 'Enter a Spell',
                ),
              ),
            ),
            Padding(
              padding: const EdgeInsets.only(bottom: 16.0),
              child: ElevatedButton(
                onPressed: _callCharm,
                child: const Text('Send to Server'),
              ),
            ),
            _ResultDisplay(
              resultMessage: _resultMessage,
              errorMessage: _errorMessage,
            ),
          ],
        ),
      ),
    );
  }
}

// _ResultDisplays shows the result of the call. Either the returned result from
// the `example.hello` endpoint method or an error message.
class _ResultDisplay extends StatelessWidget {
  final String? resultMessage;
  final String? errorMessage;

  const _ResultDisplay({
    this.resultMessage,
    this.errorMessage,
  });

  
  Widget build(BuildContext context) {
    String text;
    Color backgroundColor;
    if (errorMessage != null) {
      backgroundColor = Colors.red[300]!;
      text = errorMessage!;
    } else if (resultMessage != null) {
      backgroundColor = Colors.green[300]!;
      text = resultMessage!;
    } else {
      backgroundColor = Colors.grey[300]!;
      text = 'No server response yet.';
    }

    return Container(
      height: 50,
      color: backgroundColor,
      child: Center(
        child: Text(text),
      ),
    );
  }
}
  • podpod_serverのendpointに関数を書いてgenerateする。
  • podpod_clientのclient.dartが同期。
  • podpod_flutterのmain.dartから読んでいるのは、client.dart。

今、main.dartはtextcontrollerとかあるのでstateful widgetだけれど、これを今後、model使ってproviderでとか、riverpodでとか言い出したら、endpointやclientとの関係はどうなるんだろう。今の私にはさっぱりわからない。

結局

わざわざ動画を掲載するほどおもしろい結果にはまだ至っていない。ましてこれを、今までつくってきたアプリの全面改装として使うのには、まだまだ長い苦闘がありそう。

でもまあ、仕方がないね。前進あるのみ。

Flutter大学

Discussion