main.dart 8.16 KB
Newer Older
1 2 3 4 5 6
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';
import 'dart:io';
7
import 'dart:math';
8 9 10 11 12 13 14 15 16 17
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter/services.dart';

/// An example that sets up local http server for serving single
/// image, creates single flutter widget with five copies of requested
/// image and prints how long the loading took.
///
/// This is used in [$FH/flutter/devicelab/bin/tasks/image_list_reported_duration.dart] test.
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
///
String certificate = '''
-----BEGIN CERTIFICATE-----
MIIDlTCCAn2gAwIBAgIUNfw2s1D9qwYw8bMEZ1rdNVQXi9YwDQYJKoZIhvcNAQEL
BQAwcjELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE5ldyBZb3JrMRIwEAYDVQQHDAlS
b2NoZXN0ZXIxEjAQBgNVBAoMCWxvY2FsaG9zdDEUMBIGA1UECwwLRGV2ZWxvcG1l
bnQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xOTA4MjIyMzMwMjdaFw0yMDA4MjEy
MzMwMjdaMHIxCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhOZXcgWW9yazESMBAGA1UE
BwwJUm9jaGVzdGVyMRIwEAYDVQQKDAlsb2NhbGhvc3QxFDASBgNVBAsMC0RldmVs
b3BtZW50MRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQCi/fmozdYuCIZbJS7y4zYPp2NRboLXrpUcUzzvzz+24k/TYUPN
eRvf6wiNXHvr1ijMg1j3wQP72RphxI7cY7XCwzRiM5QeQy3EtRz4ETYBzOev3mHL
LEgZ9RnSq/u42siSS9CNjoz97liPyQUq8h37/09qhYG0hR/2pRN+YB9g7sNYoGe2
B7zkh3azRS0/LtgltXwHUId7QzJc15W9Q7adsNVTpOCo7dOj2KWz6sEtFGkYfwLV
5uiTslRdWCCOUD9iZjCtlPqALkGnWyhNiFJESLbVNC6MURyMngcALW0JTMwc2oDj
MxtdNkMl0cdzPlhXMDKIKpY9bWbRKUUdsfOnAgMBAAGjIzAhMB8GA1UdEQQYMBaC
CWxvY2FsaG9zdIIJMTI3LjAuMC4xMA0GCSqGSIb3DQEBCwUAA4IBAQAOYegYQ05v
S/220FvmwH2X+/r0rcjJ5ZyOGq/MTNMhqXZKHgcNELpOLpDMZVvseXHeUKDrqZFi
4aemMT5zbuYy3yRkQ/X39GEsksi2/Ii6Xk2zw6IJGL6hvneJEbUP3LN1POy2JLJ/
Wt7Uda8CWa+H9+0sognT6+/86Q2fWMYnFFnAQk5wW4pYDlgInupoXzjcG0kmPgOO
ijFsQ67xa0nUn1Llviy3pHX50IU2C+cwlWKNgxfmj6HKG9XIlu8gC/R+Ruf4aSUZ
QT170jY8Lf6PzqLmbIW86tcKftbkP5RiEp8ESg9jDdNjhwZjxlF13aAVE8uJxP0j
I12tWIG+68AM
-----END CERTIFICATE-----
''';

String privateKey = '''
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCi/fmozdYuCIZb
JS7y4zYPp2NRboLXrpUcUzzvzz+24k/TYUPNeRvf6wiNXHvr1ijMg1j3wQP72Rph
xI7cY7XCwzRiM5QeQy3EtRz4ETYBzOev3mHLLEgZ9RnSq/u42siSS9CNjoz97liP
yQUq8h37/09qhYG0hR/2pRN+YB9g7sNYoGe2B7zkh3azRS0/LtgltXwHUId7QzJc
15W9Q7adsNVTpOCo7dOj2KWz6sEtFGkYfwLV5uiTslRdWCCOUD9iZjCtlPqALkGn
WyhNiFJESLbVNC6MURyMngcALW0JTMwc2oDjMxtdNkMl0cdzPlhXMDKIKpY9bWbR
KUUdsfOnAgMBAAECggEADUStiS9Qayjofwz02HLkmLugmyVq41Hj841XHZJ6dlHP
+74kPdrJCR5h8NgBgn5JjfR3TpvYziyrOCA/HPPE/RjU79WRDjGbzTKNLCiCg/0B
M1DgFyEAsZRBSOQVNsQgpcAkNxHOqnE3pmTP1eIlzLjI5zv9Bgv8QSDJCHWcuFA2
NrvGudq3dlFnZwjipx0k0E1hCsaClqLsi5jEXIBA6TX7RTeeXjC+j2/DlmTpBxo0
c34o/sSoCl+mfJDQ3QApXLFuycBl7nauO0M+VsUWrKYqHHr4NIcgoIpIX28QjWc/
Y2+iooMSBO1ToK2nPD8hZQwNDF8xz6Xf7QNkTOpnqQKBgQDNgfNYkEvgWxhSMmW7
cK06supZC/isj2JfQmlJc80JcAvf8rpynZLi4XWZWRo0PykM4szI91h3YHTG27VX
YVHSsFs+6FwP7fLAHR7FYn65p/bwTuqpWGjcmQhc0HWx7y45HRIoSK/m+Z7lNA/J
QxnCp1khTmhPqCNglo+38vZSzQKBgQDLCeBhji0K/BOmCrhNxRxrCvHYEd6gqRtC
+rKhco6mMrxma/UmggRKoGXg0yMcya3199y3pDkumHSlcMdUb+I9b+3j8R6ivoqN
TI1ned5K1uq3FyZpD0dZQWuunVeqYXuUQw5y5GxvK7haohbVpUG6lC3qYq2ubiYC
D0ENUfNoQwKBgEGucOozZCzWsJVEykL4JkWGfWPscZQlV5l+jkwNmNCVYRY4a+LJ
/fJJgN58HeXo8ePOcQkiFMJCr9AG1JSS5CXke6VFencU4+sG45jOfBY2WrQ/ZLyv
JwSqXIPdlGBEQ4+5fN4nLSEzUteKpij7KzaNae09NBWRdY0fUdvG6XdZAoGAf02S
/TfKsB97JlmEU2aqOcdj+WjC4JMG/8j2JVoRbM1U6Rb5X4qXrD7DgeKAGnWteBJP
tmjmXXvDb1O19xArlv/N9WRiJAI6FvwPkPiNUvlLsz51m9uzjZgCLzqCE9cJR91/
erQT9ORBs7n7fTsfah+sZlA2u65ecF4mGHbwmccCgYAqHNRHnx1iHrYfr97cmXc9
fNjJ7e1NHhVdgpGjaOiBSKj2rHNRy6iwCNbs5wjmRWlgqnFEM5r0VfFn9L0PvcQK
7iExMTm/PkSqHUntpy82Q8zRWmhw0G5p9DYyIPtaeW1NIKpIlCw6dTlf750BiGkr
mhBKvYQc85gja0s1c+1VXA==
-----END PRIVATE KEY-----
''';

class MyHttpOverrides extends HttpOverrides {
  @override
  HttpClient createHttpClient(SecurityContext context) {
    return super.createHttpClient(
        (context ?? SecurityContext())..setTrustedCertificatesBytes(certificate.codeUnits)
    );
  }
}

84
Future<void> main() async {
85 86 87 88 89 90
  HttpOverrides.global = MyHttpOverrides();

  final SecurityContext serverContext = SecurityContext()
    ..useCertificateChainBytes(certificate.codeUnits)
    ..usePrivateKeyBytes(privateKey.codeUnits);

91
  final HttpServer httpServer =
92
      await HttpServer.bindSecure('localhost', 0, serverContext);
93 94 95
  final int port = httpServer.port;
  print('Listening on port $port.');

96 97
  // Initializes bindings before using any platform channels.
  WidgetsFlutterBinding.ensureInitialized();
98 99
  final ByteData byteData = await rootBundle.load('images/coast.jpg');
  httpServer.listen((HttpRequest request) async {
100 101 102 103 104 105 106 107 108 109
    const int chunk_size = 2048;
    int offset = byteData.offsetInBytes;
    while (offset < byteData.lengthInBytes) {
      final int length = min(byteData.lengthInBytes - offset, chunk_size);
      final Uint8List bytes = byteData.buffer.asUint8List(offset, length);
      offset += length;
      request.response.add(bytes);
      // Let other isolates and microtasks to run.
      await Future<void>.delayed(const Duration());
    }
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
    request.response.close();
  });

  runApp(MyApp(port));
}

const int IMAGES = 50;

@immutable
class MyApp extends StatelessWidget {
  const MyApp(this.port);

  final int port;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page', port: port),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key key, this.title, this.port}) : super(key: key);
  final String title;
  final int port;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  Widget createImage(final int index, final Completer<bool> completer) {
    return Image.network(
156
        'https://localhost:${widget.port}/${_counter * IMAGES + index}',
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
        frameBuilder: (
          BuildContext context,
          Widget child,
          int frame,
          bool wasSynchronouslyLoaded,
        ) {
          if (frame == 0 && !completer.isCompleted) {
            completer.complete(true);
          }
          return child;
        },
    );
  }

  @override
  Widget build(BuildContext context) {
    final List<AnimationController> controllers = List<AnimationController>(IMAGES);
    for (int i = 0; i < IMAGES; i++) {
      controllers[i] = AnimationController(
        duration: const Duration(milliseconds: 3600),
        vsync: this,
      )..repeat();
    }
    final List<Completer<bool>> completers = List<Completer<bool>>(IMAGES);
    for (int i = 0; i < IMAGES; i++) {
      completers[i] = Completer<bool>();
    }
    final List<Future<bool>> futures = completers.map(
        (Completer<bool> completer) => completer.future).toList();
    final DateTime started = DateTime.now();
    Future.wait(futures).then((_) {
      print(
          '===image_list=== all loaded in ${DateTime.now().difference(started).inMilliseconds}ms.');
    });
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Row(children: createImageList(IMAGES, completers, controllers)),
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }

  List<Widget> createImageList(int count, List<Completer<bool>> completers,
      List<AnimationController> controllers) {
    final List<Widget> list = <Widget>[];
    for (int i = 0; i < count; i++) {
      list.add(Flexible(
          fit: FlexFit.tight,
          flex: i + 1,
          child: RotationTransition(
              turns: controllers[i],
              child: createImage(i + 1, completers[i]))));
    }
    return list;
  }
}