// Copyright 2020 The Flutter team. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'package:flutter/material.dart'; import 'package:flutter_module_books/api.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({Key key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( primaryColor: const Color(0xff6200ee), ), home: const BookDetail(), ); } } typedef BookReceived = void Function(Book book); class FlutterBookApiHandler extends FlutterBookApi { FlutterBookApiHandler(this.callback); final BookReceived callback; @override void displayBookDetails(Book book) { assert( book != null, 'Non-null book expected from FlutterBookApi.displayBookDetails call.', ); callback(book); } } class BookDetail extends StatefulWidget { const BookDetail({this.hostApi, this.flutterApi, Key key}) : super(key: key); // These are the outgoing and incoming APIs that are here for injection for // tests. final HostBookApi hostApi; final FlutterBookApi flutterApi; @override _BookDetailState createState() => _BookDetailState(); } class _BookDetailState extends State { Book book; HostBookApi hostApi; FocusNode textFocusNode = FocusNode(); TextEditingController titleTextController = TextEditingController(); TextEditingController subtitleTextController = TextEditingController(); TextEditingController authorTextController = TextEditingController(); @override void initState() { super.initState(); // This `HostBookApi` class instance lets us make outgoing calls to the // platform. hostApi = widget.hostApi ?? HostBookApi(); // Registering this `FlutterBookApiHandler` class lets us receive incoming // calls from the platform. // TODO(gaaclarke): make the setup method an instance method so it's // injectable https://github.com/flutter/flutter/issues/59119. FlutterBookApi.setup(FlutterBookApiHandler( // The `FlutterBookApi` just has one method. Just give a closure for that // method to the handler class. (book) { setState(() { // This book model is what we're going to return to Kotlin eventually. // Keep it bound to the UI. this.book = book; titleTextController.text = book.title; titleTextController.addListener(() { this.book?.title = titleTextController.text; }); // Subtitle could be null. // TODO(gaaclarke): https://github.com/flutter/flutter/issues/59118. subtitleTextController.text = book.subtitle ?? ''; subtitleTextController.addListener(() { this.book?.subtitle = subtitleTextController.text; }); authorTextController.text = book.author; authorTextController.addListener(() { this.book?.author = authorTextController.text; }); }); })); } void clear() { book = null; // Keep focus if going to the home screen but unfocus if leaving // the activity. textFocusNode.unfocus(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Book Details'), leading: IconButton( icon: const Icon(Icons.clear), // Pressing clear cancels the edit and leaves the activity without // modification. onPressed: () { hostApi.cancel(); clear(); }, ), actions: [ IconButton( icon: const Icon(Icons.check), // Pressing save sends the updated book to the platform. onPressed: () { hostApi.finishEditingBook(book); clear(); }, ), ], ), body: book == null // Draw a spinner until the platform gives us the book to show details // for. ? const Center(child: CircularProgressIndicator()) : Focus( focusNode: textFocusNode, child: ListView( padding: const EdgeInsets.all(24), children: [ TextField( controller: titleTextController, decoration: const InputDecoration( border: OutlineInputBorder(), filled: true, hintText: "Title", labelText: "Title", ), ), const SizedBox(height: 24), TextField( controller: subtitleTextController, maxLines: 2, decoration: const InputDecoration( border: OutlineInputBorder(), filled: true, hintText: "Subtitle", labelText: "Subtitle", ), ), const SizedBox(height: 24), TextField( controller: authorTextController, decoration: const InputDecoration( border: OutlineInputBorder(), filled: true, hintText: "Author", labelText: "Author", ), ), const SizedBox(height: 32), const Divider(), Center( child: Padding( padding: const EdgeInsets.all(8.0), child: Text( '${book.pageCount} pages ~ published ${book.publishDate}'), ), ), const Divider(), const SizedBox(height: 32), const Center( child: Text( 'BOOK DESCRIPTION', style: TextStyle( fontSize: 15, fontWeight: FontWeight.bold, decoration: TextDecoration.underline, ), ), ), const SizedBox(height: 12), Text( book.summary, style: TextStyle(color: Colors.grey.shade600, height: 1.24), ), ], ), ), ); } }