.NET 開発基盤部会 Wiki」は、「Open棟梁Project」,「OSSコンソーシアム .NET開発基盤部会」によって運営されています。

目次

概要

WebAPIを経由した簡単なCRUD処理の実装

詳細

WebAPI

プロンプト

添付のReactプロジェクトのコードをFlutterに移植します。
なお、作成したScreenは以下からリンクします。

※ 添付のReactプロジェクトのコード:https://github.com/OpenTouryoProject/FrontendTemplates/blob/develop/UI/SPA/React/vite-react-ts/src/pages/FetchData.tsx

※ 「以下からリンクします。」の以下:https://github.com/OpenTouryoProject/FrontendTemplates/blob/develop/UI/XPlat/flutter_template/lib/layout/main_layout.dart

インストール

WeatherForecast?

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

// --- モデル ---
class WeatherForecast {
  final String dateFormatted;
  final int temperatureC;
  final int temperatureF;
  final String summary;

  const WeatherForecast({
    required this.dateFormatted,
    required this.temperatureC,
    required this.temperatureF,
    required this.summary,
  });

  factory WeatherForecast.fromJson(Map<String, dynamic> json) {
    return WeatherForecast(
      dateFormatted: json['dateFormatted'] as String,
      temperatureC: json['temperatureC'] as int,
      temperatureF: json['temperatureF'] as int,
      summary: json['summary'] as String,
    );
  }
}

// --- 定数 (constants.dart の FetchDataRootUrl に相当) ---
// 実際の URL に合わせて変更してください。
const String _fetchDataRootUrl =
    'https://localhost:44335/api/SampleData/WeatherForecasts?';

// --- Screen ---
class ScreenFetchData extends StatefulWidget {
  const ScreenFetchData({super.key});

  @override
  State<ScreenFetchData> createState() => _ScreenFetchDataState();
}

class _ScreenFetchDataState extends State<ScreenFetchData> {
  List<WeatherForecast> _forecasts = [];
  bool _loading = true;
  int _currentPage = 1;
  String? _errorMessage;

  @override
  void initState() {
    super.initState();
    _fetchForecasts(1);
  }

  Future<void> _fetchForecasts(int page) async {
    setState(() {
      _loading = true;
      _errorMessage = null;
    });

    final uri = Uri.parse('${_fetchDataRootUrl}startDateIndex=$page');

    try {
      // oauth_oidc.createHttpRequestHeader(false) に相当するヘッダーを設定してください。
      final response = await http.get(
        uri,
        headers: {
          'Content-Type': 'application/json',
          // 'Authorization': 'Bearer <token>',  // 認証トークンが必要な場合
        },
      );

      if (response.statusCode == 200) {
        final List<dynamic> jsonList =
            jsonDecode(response.body) as List<dynamic>;
        final forecasts = jsonList
            .map((e) => WeatherForecast.fromJson(e as Map<String, dynamic>))
            .toList();
        setState(() {
          _forecasts = forecasts;
          _currentPage = page;
          _loading = false;
        });
      } else {
        throw Exception('HTTP ${response.statusCode}');
      }
    } catch (e) {
      debugPrint('fetch error: $e');
      setState(() {
        _errorMessage = e.toString();
        _loading = false;
      });
    }
  }

  void _handlePrev() {
    if (_currentPage > 1) {
      _fetchForecasts(_currentPage - 1);
    }
  }

  void _handleNext() {
    _fetchForecasts(_currentPage + 1);
  }

  // --- テーブル描画 ---
  Widget _buildForecastTable() {
    const headerStyle = TextStyle(fontWeight: FontWeight.bold);

    return SingleChildScrollView(
      scrollDirection: Axis.horizontal,
      child: DataTable(
        columns: const [
          DataColumn(label: Text('Date', style: headerStyle)),
          DataColumn(label: Text('Temp. (C)', style: headerStyle)),
          DataColumn(label: Text('Temp. (F)', style: headerStyle)),
          DataColumn(label: Text('Summary', style: headerStyle)),
        ],
        rows: _forecasts
            .map(
              (f) => DataRow(cells: [
                DataCell(Text(f.dateFormatted)),
                DataCell(Text('${f.temperatureC}')),
                DataCell(Text('${f.temperatureF}')),
                DataCell(Text(f.summary)),
              ]),
            )
            .toList(),
      ),
    );
  }

  // --- ページネーション ---
  Widget _buildPagination() {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        ElevatedButton(
          onPressed: (_loading || _currentPage <= 1) ? null : _handlePrev,
          child: const Text('« Prev'),
        ),
        const SizedBox(width: 12),
        Text('Page $_currentPage'),
        const SizedBox(width: 12),
        ElevatedButton(
          onPressed: _loading ? null : _handleNext,
          child: const Text('Next »'),
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Text(
            'Weather forecast',
            style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
          ),
          const SizedBox(height: 8),
          const Text('This component demonstrates fetching data from the server.'),
          const SizedBox(height: 16),

          // コンテンツ
          Expanded(
            child: Center(
              child: _loading
                  ? const CircularProgressIndicator()
                  : _errorMessage != null
                      ? Text(
                          'Error: $_errorMessage',
                          style: const TextStyle(color: Colors.red),
                        )
                      : _buildForecastTable(),
            ),
          ),

          const SizedBox(height: 12),
          _buildPagination(),
        ],
      ),
    );
  }
}

CRUD

参考


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS