add apibuilder widget to make api callsmore straightforward and data updateable from everywhere through providers
This commit is contained in:
		@@ -1,7 +1,10 @@
 | 
			
		||||
import 'dart:async';
 | 
			
		||||
 | 
			
		||||
import 'package:aurcache/api/builds.dart';
 | 
			
		||||
import 'package:aurcache/components/build_output.dart';
 | 
			
		||||
import 'package:aurcache/models/build.dart';
 | 
			
		||||
import 'package:aurcache/providers/APIBuilder.dart';
 | 
			
		||||
import 'package:aurcache/providers/build_provider.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:go_router/go_router.dart';
 | 
			
		||||
 | 
			
		||||
@@ -18,146 +21,54 @@ class BuildScreen extends StatefulWidget {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _BuildScreenState extends State<BuildScreen> {
 | 
			
		||||
  late Future<Build> buildData;
 | 
			
		||||
  late Future<String> initialOutput;
 | 
			
		||||
 | 
			
		||||
  String output = "";
 | 
			
		||||
  Timer? outputTimer, buildDataTimer;
 | 
			
		||||
  final scrollController = ScrollController();
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      body: FutureBuilder(
 | 
			
		||||
          future: buildData,
 | 
			
		||||
          builder: (context, snapshot) {
 | 
			
		||||
            if (snapshot.hasData) {
 | 
			
		||||
              final buildData = snapshot.data!;
 | 
			
		||||
 | 
			
		||||
              return Column(
 | 
			
		||||
                crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                mainAxisAlignment: MainAxisAlignment.start,
 | 
			
		||||
                children: [
 | 
			
		||||
                  Row(
 | 
			
		||||
                    mainAxisAlignment: MainAxisAlignment.start,
 | 
			
		||||
                    children: [
 | 
			
		||||
                      const SizedBox(
 | 
			
		||||
                        width: 10,
 | 
			
		||||
                      ),
 | 
			
		||||
                      IconButton(
 | 
			
		||||
                        icon: Icon(
 | 
			
		||||
                          switchSuccessIcon(buildData.status),
 | 
			
		||||
                          color: switchSuccessColor(buildData.status),
 | 
			
		||||
                        ),
 | 
			
		||||
                        onPressed: () {
 | 
			
		||||
                          context.replace("/build/${buildData.id}");
 | 
			
		||||
                        },
 | 
			
		||||
                      ),
 | 
			
		||||
                      const SizedBox(
 | 
			
		||||
                        width: 10,
 | 
			
		||||
                      ),
 | 
			
		||||
                      Text(
 | 
			
		||||
                        buildData.pkg_name,
 | 
			
		||||
                        style: const TextStyle(fontWeight: FontWeight.bold),
 | 
			
		||||
                      ),
 | 
			
		||||
                      const SizedBox(
 | 
			
		||||
                        width: 10,
 | 
			
		||||
                      ),
 | 
			
		||||
                      const Text("triggered 2 months ago")
 | 
			
		||||
                    ],
 | 
			
		||||
                  ),
 | 
			
		||||
                  const SizedBox(
 | 
			
		||||
                    height: 15,
 | 
			
		||||
                  ),
 | 
			
		||||
                  Expanded(
 | 
			
		||||
                    flex: 1,
 | 
			
		||||
                    child: SingleChildScrollView(
 | 
			
		||||
                      controller: scrollController,
 | 
			
		||||
                      scrollDirection: Axis.vertical, //.horizontal
 | 
			
		||||
                      child: Padding(
 | 
			
		||||
                        padding: const EdgeInsets.only(left: 30, right: 15),
 | 
			
		||||
                        child: Text(
 | 
			
		||||
                          output,
 | 
			
		||||
                          style: const TextStyle(
 | 
			
		||||
                            fontSize: 16.0,
 | 
			
		||||
                            color: Colors.white,
 | 
			
		||||
                          ),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
      body: APIBuilder<BuildProvider, Build, BuildDTO>(
 | 
			
		||||
          dto: BuildDTO(buildID: widget.buildID),
 | 
			
		||||
          interval: const Duration(seconds: 10),
 | 
			
		||||
          onLoad: () => const Text("no data"),
 | 
			
		||||
          onData: (buildData) {
 | 
			
		||||
            return Column(
 | 
			
		||||
              crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
              mainAxisAlignment: MainAxisAlignment.start,
 | 
			
		||||
              children: [
 | 
			
		||||
                Row(
 | 
			
		||||
                  mainAxisAlignment: MainAxisAlignment.start,
 | 
			
		||||
                  children: [
 | 
			
		||||
                    const SizedBox(
 | 
			
		||||
                      width: 10,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
              );
 | 
			
		||||
            } else {
 | 
			
		||||
              return const Text("loading build");
 | 
			
		||||
            }
 | 
			
		||||
                    IconButton(
 | 
			
		||||
                      icon: Icon(
 | 
			
		||||
                        switchSuccessIcon(buildData.status),
 | 
			
		||||
                        color: switchSuccessColor(buildData.status),
 | 
			
		||||
                      ),
 | 
			
		||||
                      onPressed: () {
 | 
			
		||||
                        context.replace("/build/${buildData.id}");
 | 
			
		||||
                      },
 | 
			
		||||
                    ),
 | 
			
		||||
                    const SizedBox(
 | 
			
		||||
                      width: 10,
 | 
			
		||||
                    ),
 | 
			
		||||
                    Text(
 | 
			
		||||
                      buildData.pkg_name,
 | 
			
		||||
                      style: const TextStyle(fontWeight: FontWeight.bold),
 | 
			
		||||
                    ),
 | 
			
		||||
                    const SizedBox(
 | 
			
		||||
                      width: 10,
 | 
			
		||||
                    ),
 | 
			
		||||
                    const Text("triggered 2 months ago")
 | 
			
		||||
                  ],
 | 
			
		||||
                ),
 | 
			
		||||
                const SizedBox(
 | 
			
		||||
                  height: 15,
 | 
			
		||||
                ),
 | 
			
		||||
                BuildOutput(build: buildData)
 | 
			
		||||
              ],
 | 
			
		||||
            );
 | 
			
		||||
          }),
 | 
			
		||||
      appBar: AppBar(),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void initState() {
 | 
			
		||||
    super.initState();
 | 
			
		||||
 | 
			
		||||
    initBuildDataLoader();
 | 
			
		||||
    initOutputLoader();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void initBuildDataLoader() {
 | 
			
		||||
    buildData = API.getBuild(widget.buildID);
 | 
			
		||||
    buildDataTimer = Timer.periodic(const Duration(seconds: 10), (t) {
 | 
			
		||||
      setState(() {
 | 
			
		||||
        buildData = API.getBuild(widget.buildID);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void initOutputLoader() {
 | 
			
		||||
    initialOutput = API.getOutput(buildID: widget.buildID);
 | 
			
		||||
    initialOutput.then((value) {
 | 
			
		||||
      setState(() {
 | 
			
		||||
        output = value;
 | 
			
		||||
      });
 | 
			
		||||
      _scrollToBottom();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    buildData.then((value) {
 | 
			
		||||
      // poll new output only if not finished
 | 
			
		||||
      if (value.status == 0) {
 | 
			
		||||
        outputTimer =
 | 
			
		||||
            Timer.periodic(const Duration(seconds: 3), (Timer t) async {
 | 
			
		||||
          print("refreshing output");
 | 
			
		||||
          final value = await API.getOutput(
 | 
			
		||||
              buildID: widget.buildID, line: output.split("\n").length);
 | 
			
		||||
          setState(() {
 | 
			
		||||
            output += value;
 | 
			
		||||
          });
 | 
			
		||||
 | 
			
		||||
          _scrollToBottom();
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void _scrollToBottom() {
 | 
			
		||||
    WidgetsBinding.instance.addPostFrameCallback((_) {
 | 
			
		||||
      // scroll to bottom
 | 
			
		||||
      final scrollPosition = scrollController.position;
 | 
			
		||||
      if (scrollPosition.viewportDimension < scrollPosition.maxScrollExtent) {
 | 
			
		||||
        scrollController.animateTo(
 | 
			
		||||
          scrollPosition.maxScrollExtent,
 | 
			
		||||
          duration: const Duration(milliseconds: 200),
 | 
			
		||||
          curve: Curves.easeOut,
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void dispose() {
 | 
			
		||||
    super.dispose();
 | 
			
		||||
    outputTimer?.cancel();
 | 
			
		||||
    buildDataTimer?.cancel();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										47
									
								
								frontend/lib/screens/builds_screen.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								frontend/lib/screens/builds_screen.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
import 'package:aurcache/components/builds_table.dart';
 | 
			
		||||
import 'package:aurcache/providers/APIBuilder.dart';
 | 
			
		||||
import 'package:aurcache/providers/builds_provider.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import '../constants/color_constants.dart';
 | 
			
		||||
import '../models/build.dart';
 | 
			
		||||
 | 
			
		||||
class BuildsScreen extends StatelessWidget {
 | 
			
		||||
  const BuildsScreen({super.key});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(),
 | 
			
		||||
      body: Padding(
 | 
			
		||||
        padding: const EdgeInsets.all(defaultPadding),
 | 
			
		||||
        child: Container(
 | 
			
		||||
          padding: const EdgeInsets.all(defaultPadding),
 | 
			
		||||
          decoration: const BoxDecoration(
 | 
			
		||||
            color: secondaryColor,
 | 
			
		||||
            borderRadius: BorderRadius.all(Radius.circular(10)),
 | 
			
		||||
          ),
 | 
			
		||||
          child: SingleChildScrollView(
 | 
			
		||||
            child: Column(
 | 
			
		||||
              crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
              children: [
 | 
			
		||||
                Text(
 | 
			
		||||
                  "All Builds",
 | 
			
		||||
                  style: Theme.of(context).textTheme.subtitle1,
 | 
			
		||||
                ),
 | 
			
		||||
                SizedBox(
 | 
			
		||||
                  width: double.infinity,
 | 
			
		||||
                  child: APIBuilder<BuildsProvider, List<Build>, Object>(
 | 
			
		||||
                      interval: const Duration(seconds: 10),
 | 
			
		||||
                      onLoad: () => const Text("no data"),
 | 
			
		||||
                      onData: (data) {
 | 
			
		||||
                        return BuildsTable(data: data);
 | 
			
		||||
                      }),
 | 
			
		||||
                )
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,8 @@
 | 
			
		||||
import 'package:aurcache/api/statistics.dart';
 | 
			
		||||
import 'package:aurcache/providers/APIBuilder.dart';
 | 
			
		||||
import 'package:aurcache/providers/stats_provider.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:provider/provider.dart';
 | 
			
		||||
 | 
			
		||||
import '../api/API.dart';
 | 
			
		||||
import '../components/dashboard/header.dart';
 | 
			
		||||
@@ -11,70 +14,70 @@ import '../components/dashboard/recent_builds.dart';
 | 
			
		||||
import '../components/dashboard/your_packages.dart';
 | 
			
		||||
import '../components/dashboard/side_panel.dart';
 | 
			
		||||
 | 
			
		||||
class DashboardScreen extends StatelessWidget {
 | 
			
		||||
  final stats = API.listStats();
 | 
			
		||||
class DashboardScreen extends StatefulWidget {
 | 
			
		||||
  @override
 | 
			
		||||
  State<DashboardScreen> createState() => _DashboardScreenState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _DashboardScreenState extends State<DashboardScreen> {
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return FutureBuilder(
 | 
			
		||||
        future: stats,
 | 
			
		||||
        builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
 | 
			
		||||
          if (snapshot.hasData) {
 | 
			
		||||
            final Stats stats = snapshot.data!;
 | 
			
		||||
 | 
			
		||||
            return SafeArea(
 | 
			
		||||
              child: SingleChildScrollView(
 | 
			
		||||
                child: Container(
 | 
			
		||||
                  padding: const EdgeInsets.all(defaultPadding),
 | 
			
		||||
                  child: Column(
 | 
			
		||||
    return APIBuilder<StatsProvider, Stats, Object>(
 | 
			
		||||
      onData: (stats) {
 | 
			
		||||
        return SafeArea(
 | 
			
		||||
          child: SingleChildScrollView(
 | 
			
		||||
            child: Container(
 | 
			
		||||
              padding: const EdgeInsets.all(defaultPadding),
 | 
			
		||||
              child: Column(
 | 
			
		||||
                children: [
 | 
			
		||||
                  const Header(),
 | 
			
		||||
                  const SizedBox(height: defaultPadding),
 | 
			
		||||
                  QuickInfoBanner(
 | 
			
		||||
                    stats: stats,
 | 
			
		||||
                  ),
 | 
			
		||||
                  const SizedBox(height: defaultPadding),
 | 
			
		||||
                  Row(
 | 
			
		||||
                    crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                    children: [
 | 
			
		||||
                      const Header(),
 | 
			
		||||
                      const SizedBox(height: defaultPadding),
 | 
			
		||||
                      QuickInfoBanner(
 | 
			
		||||
                        stats: stats,
 | 
			
		||||
                      ),
 | 
			
		||||
                      const SizedBox(height: defaultPadding),
 | 
			
		||||
                      Row(
 | 
			
		||||
                        crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                        children: [
 | 
			
		||||
                          Expanded(
 | 
			
		||||
                            flex: 5,
 | 
			
		||||
                            child: Column(
 | 
			
		||||
                              children: [
 | 
			
		||||
                                const YourPackages(),
 | 
			
		||||
                                const SizedBox(height: defaultPadding),
 | 
			
		||||
                                const RecentBuilds(),
 | 
			
		||||
                                if (Responsive.isMobile(context))
 | 
			
		||||
                                  const SizedBox(height: defaultPadding),
 | 
			
		||||
                                if (Responsive.isMobile(context))
 | 
			
		||||
                                  SidePanel(
 | 
			
		||||
                                      nrbuilds: stats.total_builds,
 | 
			
		||||
                                      nrfailedbuilds: stats.failed_builds,
 | 
			
		||||
                                      nrActiveBuilds: stats.active_builds),
 | 
			
		||||
                              ],
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
                          if (!Responsive.isMobile(context))
 | 
			
		||||
                            const SizedBox(width: defaultPadding),
 | 
			
		||||
                          // On Mobile means if the screen is less than 850 we dont want to show it
 | 
			
		||||
                          if (!Responsive.isMobile(context))
 | 
			
		||||
                            Expanded(
 | 
			
		||||
                              flex: 2,
 | 
			
		||||
                              child: SidePanel(
 | 
			
		||||
                      Expanded(
 | 
			
		||||
                        flex: 5,
 | 
			
		||||
                        child: Column(
 | 
			
		||||
                          children: [
 | 
			
		||||
                            const YourPackages(),
 | 
			
		||||
                            const SizedBox(height: defaultPadding),
 | 
			
		||||
                            const RecentBuilds(),
 | 
			
		||||
                            if (Responsive.isMobile(context))
 | 
			
		||||
                              const SizedBox(height: defaultPadding),
 | 
			
		||||
                            if (Responsive.isMobile(context))
 | 
			
		||||
                              SidePanel(
 | 
			
		||||
                                  nrbuilds: stats.total_builds,
 | 
			
		||||
                                  nrfailedbuilds: stats.failed_builds,
 | 
			
		||||
                                  nrActiveBuilds: stats.active_builds),
 | 
			
		||||
                            ),
 | 
			
		||||
                        ],
 | 
			
		||||
                      )
 | 
			
		||||
                          ],
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                      if (!Responsive.isMobile(context))
 | 
			
		||||
                        const SizedBox(width: defaultPadding),
 | 
			
		||||
                      // On Mobile means if the screen is less than 850 we dont want to show it
 | 
			
		||||
                      if (!Responsive.isMobile(context))
 | 
			
		||||
                        Expanded(
 | 
			
		||||
                          flex: 2,
 | 
			
		||||
                          child: SidePanel(
 | 
			
		||||
                              nrbuilds: stats.total_builds,
 | 
			
		||||
                              nrfailedbuilds: stats.failed_builds,
 | 
			
		||||
                              nrActiveBuilds: stats.active_builds),
 | 
			
		||||
                        ),
 | 
			
		||||
                    ],
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                  )
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
            );
 | 
			
		||||
          } else {
 | 
			
		||||
            return const Text("loading");
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
      },
 | 
			
		||||
      onLoad: () {
 | 
			
		||||
        return Text("loading");
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										114
									
								
								frontend/lib/screens/package_screen.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								frontend/lib/screens/package_screen.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,114 @@
 | 
			
		||||
import 'dart:async';
 | 
			
		||||
 | 
			
		||||
import 'package:aurcache/api/builds.dart';
 | 
			
		||||
import 'package:aurcache/api/packages.dart';
 | 
			
		||||
import 'package:aurcache/providers/APIBuilder.dart';
 | 
			
		||||
import 'package:aurcache/providers/builds_provider.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:go_router/go_router.dart';
 | 
			
		||||
 | 
			
		||||
import '../api/API.dart';
 | 
			
		||||
import '../components/builds_table.dart';
 | 
			
		||||
import '../components/confirm_popup.dart';
 | 
			
		||||
import '../constants/color_constants.dart';
 | 
			
		||||
import '../models/build.dart';
 | 
			
		||||
import '../models/package.dart';
 | 
			
		||||
import '../providers/package_provider.dart';
 | 
			
		||||
import '../providers/packages_provider.dart';
 | 
			
		||||
 | 
			
		||||
class PackageScreen extends StatefulWidget {
 | 
			
		||||
  const PackageScreen({super.key, required this.pkgID});
 | 
			
		||||
 | 
			
		||||
  final int pkgID;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  State<PackageScreen> createState() => _PackageScreenState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _PackageScreenState extends State<PackageScreen> {
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(),
 | 
			
		||||
      body: APIBuilder<PackageProvider, Package, PackageDTO>(
 | 
			
		||||
          dto: PackageDTO(pkgID: widget.pkgID),
 | 
			
		||||
          onLoad: () => const Text("loading"),
 | 
			
		||||
          onData: (pkg) {
 | 
			
		||||
            return Padding(
 | 
			
		||||
              padding: const EdgeInsets.all(defaultPadding),
 | 
			
		||||
              child: Column(
 | 
			
		||||
                crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                children: [
 | 
			
		||||
                  Row(
 | 
			
		||||
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
			
		||||
                    crossAxisAlignment: CrossAxisAlignment.center,
 | 
			
		||||
                    children: [
 | 
			
		||||
                      Container(
 | 
			
		||||
                        margin: const EdgeInsets.only(left: 15),
 | 
			
		||||
                        child: Text(
 | 
			
		||||
                          pkg.name,
 | 
			
		||||
                          style: const TextStyle(fontSize: 32),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                      Container(
 | 
			
		||||
                        margin: const EdgeInsets.only(right: 15),
 | 
			
		||||
                        child: ElevatedButton(
 | 
			
		||||
                          onPressed: () async {
 | 
			
		||||
                            final confirmResult =
 | 
			
		||||
                                await showDeleteConfirmationDialog(context);
 | 
			
		||||
                            if (!confirmResult) return;
 | 
			
		||||
 | 
			
		||||
                            final succ = await API.deletePackage(pkg.id);
 | 
			
		||||
                            if (succ) {
 | 
			
		||||
                              context.pop();
 | 
			
		||||
                            }
 | 
			
		||||
                          },
 | 
			
		||||
                          child: const Text(
 | 
			
		||||
                            "Delete",
 | 
			
		||||
                            style: TextStyle(color: Colors.redAccent),
 | 
			
		||||
                          ),
 | 
			
		||||
                        ),
 | 
			
		||||
                      )
 | 
			
		||||
                    ],
 | 
			
		||||
                  ),
 | 
			
		||||
                  const SizedBox(
 | 
			
		||||
                    height: 25,
 | 
			
		||||
                  ),
 | 
			
		||||
                  Container(
 | 
			
		||||
                    padding: const EdgeInsets.all(defaultPadding),
 | 
			
		||||
                    decoration: const BoxDecoration(
 | 
			
		||||
                      color: secondaryColor,
 | 
			
		||||
                      borderRadius: BorderRadius.all(Radius.circular(10)),
 | 
			
		||||
                    ),
 | 
			
		||||
                    child: SingleChildScrollView(
 | 
			
		||||
                      child: Column(
 | 
			
		||||
                        crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                        children: [
 | 
			
		||||
                          Text(
 | 
			
		||||
                            "Builds of ${pkg.name}",
 | 
			
		||||
                            style: Theme.of(context).textTheme.subtitle1,
 | 
			
		||||
                          ),
 | 
			
		||||
                          SizedBox(
 | 
			
		||||
                            width: double.infinity,
 | 
			
		||||
                            child: APIBuilder<BuildsProvider, List<Build>,
 | 
			
		||||
                                BuildsDTO>(
 | 
			
		||||
                              key: GlobalKey(),
 | 
			
		||||
                              dto: BuildsDTO(pkgID: pkg.id),
 | 
			
		||||
                              interval: const Duration(seconds: 5),
 | 
			
		||||
                              onData: (data) {
 | 
			
		||||
                                return BuildsTable(data: data);
 | 
			
		||||
                              },
 | 
			
		||||
                              onLoad: () => const Text("no data"),
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
                        ],
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                  )
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
            );
 | 
			
		||||
          }),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user