Komt er écht een compleet nieuwe versie van de Wateengast-app aan?

Breaking news! De geruchtenmolen draait al geruime tijd op volle toeren en ook alle techsites voelen aan hun wateren dat er iets belangrijks aan zit te komen. Iets groots, iets geweldigs, iets wereldschokkends. Lange tijd heb ik mij op de vlakte gehouden, maar vandaag kan ik het niet langer geheim houden. Het is waar. Ik beken! Inderdaad… er komt een compleet nieuwe versie aan de de enige echte Wateengast-app.

Coming soon, to a phone near you!

De trouwe bezoeker van deze site was het misschien al opgevallen dat ik een tijdje geleden een warrig verhaal hield over een geweldig leuk framework Flutter en de daarvoor gebruikte programmeertaal Dart. Inmiddels zijn we alweer een paar weken verder en in de tussentijd heb ik de nodige uurtjes gestoken in mijn nieuwste hobby-project: het from scratch bouwen van een eigen Wateengast-app!

Wat is er anders dan vorige keer?
De huidige versie van mijn app is gebouwd met Ionic, een soortgelijk framework als Flutter, maar dan compleet geschreven in Javascript. Die app heb ik gebouwd tijdens een cursus, maar daarmee leerde ik niet echt hoe de boel precies werkte. Het doel was primair die app, niet het leren van de taal. Dit keer pak ik dat compleet anders aan, met Flutter!

Wat is er dan zo leuk aan Flutter?
Flutter heeft meerdere leuke punten, maar dit zijn voor mij de belangrijkste.

  1. Doordat Flutter gebruik maakt van het Material design ziet het er heel mooi uit.
  2. Het is ontwikkeld door de mensen van Google en is daardoor naadloos te integreren met haar andere diensten (zoals Firestore)
  3. Alles op het scherm is opgebouwd in Widgets en hangt als een soort stamboom aan elkaar
  4. daardoor codeert het heerlijk!
  5. En al helemaal in combinatie met de zogenaamde ‘hot reload’: je drukt op de knop en ziet je veranderingen direct op je telefoon

En waar zit vooral de uitdaging?
Nou ja, hoe mooi en gaaf zo’n nieuwe taal en framework ook zijn: je leert het natuurlijk niet in één dag. Ik heb al ontelbaar veel dingetjes geprobeerd en voor je het weet ben je uren aan het spelen om de leukste features gewoon allemaal eens uit te proberen. Het is de kunst om niet te veel af te dwalen van je doel en daar gericht naartoe te werken. Dan kom je al genoeg hobbels tegen.

Wat voor hobbels?
Voor mijn app maak ik gebruik van de API die standaard bij WordPress-sites wordt meegeleverd. Dat is een manier om automatisch je posts op te halen en naar bijvoorbeeld de afbeeldingen op je site te werken. Voor mij was het soms nog best een gepuzzel om die data op te halen (asynchroon met je code, het duurt immers even voordat de data is opgehaald). En daarna moet je die data uitpakken en vertalen naar een mooie layout. Totdat dat eenmaal werkt ben je toch alweer een tijdje verder. Maar nu ik eenmaal weet hoe dat werkt, zijn de mogelijkheden eindeloos! Kom maar door met die API’s 🙂

En wat nog meer?
Een tweede flinke breinbreker was de zogenaamde ‘infinite scroll’, oftewel het automatisch ophalen van nieuwe posts als je naar de onderkant van een lijst bent gescrold. Dat werkt in je code nog niet zo makkelijk als je zou denken, maar ook die berg heb ik overwonnen. De eindstreep is nu werkelijk in zicht!

Wat gaat er nu gebeuren?
Met nog een paar losse eindjes te gaan, is het bijna tijd om mijn Minimal Viable Product in te gaan pakken voor de Android Play Store. Daarna ga ik zeker nog door met extra features toevoegen, maar ik zou het geweldig vinden als mijn app volgende week klaar is voor het vieren van mijn 1000ste post op Wateengast (ja inderdaad: sodeknetters!)

Tot die tijd nog even lekker door coderen in die beperkte vrije uurtjes die ik heb. Maar het eindresultaat mag er zijn, die spoiler durf ik alvast te verklappen!

Stay tuned! Dan deel ik ook alle code en kunnen jullie zien hoe het eraan toe is gegaan. Mocht je dat soort dingen interessant vinden uiteraard 😉

Vooruit, voor de nerds onder ons hier een sneak preview van de code van het openingsscherm. De code is nog niet goptemaliseerd dus moet ik nog wat refactoren, maar je begrijpt het idee ;-):

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'dart:async';
import 'dart:convert';
import 'package:wateengast/models/singlepost.dart';

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

List<SinglePost> parsePosts(response) {
  final parsed = jsonDecode(response).cast<Map<String, dynamic>>();
  return parsed.map<SinglePost>((json) => SinglePost.fromJson(json)).toList();
}

class _HomeState extends State<Home> {
  Future<List> futurePostList;
  ScrollController _scrollController = new ScrollController();
  bool loading = false;
  int page = 1;
  List currentPostList = [];

  Future<List<SinglePost>> _getPosts() async {
    var queryParameters = {
      '_embed': '',
      'per_page': '30',
      'page': page.toString(),
    };

    var uri = Uri.https('www.wateengast.nl', '/wp-json/wp/v2/posts', queryParameters);

    final response = await http.get(uri);
    return compute(parsePosts, response.body);
  }

  @override
  void initState() {
    super.initState();
    futurePostList = _getPosts();
    _getPosts().then((response) {
      currentPostList = response;
      setState(() {});
    });

    _scrollController.addListener(() {
      if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
        page += 1;
        _getPosts().then((response) {
          currentPostList.addAll(response);
          setState(() {});
        });
      }
    });
  }

  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Wat een gast...'),
        backgroundColor: Colors.green, //#4CAF50 RGB 76 175 80
        centerTitle: true,
      ),
      body: Center(
          child: ListView.separated(
              controller: _scrollController,
              itemCount: currentPostList.length + 1,
              separatorBuilder: (BuildContext context, int index) => Divider(),
              padding: EdgeInsets.all(10),
              itemBuilder: (context, index) {
                if (currentPostList.length == 0) {
                  return Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      SizedBox(height: 200),
                      Text('Wat een gast!',
                          style: TextStyle(
                            fontSize: 24,
                          )),
                      Padding(
                        padding: const EdgeInsets.all(15.0),
                        child: SpinKitThreeBounce(
                          color: Colors.green,
                        ),
                      ),
                    ],
                  );
                } else if (index == currentPostList.length) {
                  return new ListTile(
                    leading: Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: CircularProgressIndicator(),
                    ),
                    title: Text('Een momentje!'),
                    subtitle: Text('Er worden meer posts opgehaald...'),
                  );
                } else {
                  return new ListTile(
                      title: Text(currentPostList[index].title),
                      leading: ClipRRect(
                        borderRadius: BorderRadius.circular(6.0),
                        child: FadeInImage.assetNetwork(
                          placeholder: 'images/loadingbox.gif',
                          image: currentPostList[index].thumbnail,
                        ),
                      ),
                      contentPadding: EdgeInsets.fromLTRB(15, 4, 5, 7),
                      onTap: () {
                        Navigator.pushNamed(
                          context,
                          '/postdetail',
                          arguments: {
                            'title': currentPostList[index].title,
                            'image': currentPostList[index].image,
                            'content': currentPostList[index].content,
                          },
                        );
                      });
                }
              })),
    );
  }
}

You May Also Like

1 Comment

  1. Kom maar door met die code op Github 😉 Altijd leuk om te zien dit soort projecten. Had zelf ook al eens naar Flutter gekeken, maar was er nog niet ingedoken.

Leave a Reply

Your email address will not be published. Required fields are marked *