1// Copyright 2015 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5import 'package:flutter/material.dart'; 6 7typedef _TextTransformer = Widget Function(String name, String text); 8 9// From https://en.wikiquote.org/wiki/2001:_A_Space_Odyssey_(film) 10const String _kDialogText = ''' 11Dave: Open the pod bay doors, please, HAL. Open the pod bay doors, please, HAL. Hello, HAL. Do you read me? Hello, HAL. Do you read me? Do you read me, HAL? 12HAL: Affirmative, Dave. I read you. 13Dave: Open the pod bay doors, HAL. 14HAL: I'm sorry, Dave. I'm afraid I can't do that. 15Dave: What's the problem? 16HAL: I think you know what the problem is just as well as I do. 17Dave: What are you talking about, HAL? 18HAL: This mission is too important for me to allow you to jeopardize it.'''; 19 20// [["Dave", "Open the pod bay..."] ...] 21final List<List<String>> _kNameLines = _kDialogText 22 .split('\n') 23 .map<List<String>>((String line) => line.split(':')) 24 .toList(); 25 26final TextStyle _kDaveStyle = TextStyle(color: Colors.indigo.shade400, height: 1.8); 27final TextStyle _kHalStyle = TextStyle(color: Colors.red.shade400, fontFamily: 'monospace'); 28const TextStyle _kBold = TextStyle(fontWeight: FontWeight.bold); 29const TextStyle _kUnderline = TextStyle( 30 decoration: TextDecoration.underline, 31 decorationColor: Color(0xFF000000), 32 decorationStyle: TextDecorationStyle.wavy, 33); 34 35Widget toStyledText(String name, String text) { 36 final TextStyle lineStyle = (name == 'Dave') ? _kDaveStyle : _kHalStyle; 37 return RichText( 38 key: Key(text), 39 text: TextSpan( 40 style: lineStyle, 41 children: <TextSpan>[ 42 TextSpan( 43 style: _kBold, 44 children: <TextSpan>[ 45 TextSpan( 46 style: _kUnderline, 47 text: name, 48 ), 49 const TextSpan(text: ':'), 50 ], 51 ), 52 TextSpan(text: text), 53 ], 54 ), 55 ); 56} 57 58Widget toPlainText(String name, String text) => Text(name + ':' + text); 59 60class SpeakerSeparator extends StatelessWidget { 61 @override 62 Widget build(BuildContext context) { 63 return Container( 64 constraints: const BoxConstraints.expand(height: 0.0), 65 margin: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 64.0), 66 decoration: const BoxDecoration( 67 border: Border( 68 bottom: BorderSide(color: Color.fromARGB(24, 0, 0, 0)) 69 ) 70 ), 71 ); 72 } 73} 74 75class StyledTextDemo extends StatefulWidget { 76 @override 77 _StyledTextDemoState createState() => _StyledTextDemoState(); 78} 79 80class _StyledTextDemoState extends State<StyledTextDemo> { 81 @override 82 void initState() { 83 super.initState(); 84 _toText = toStyledText; 85 } 86 87 _TextTransformer _toText; 88 89 void _handleTap() { 90 setState(() { 91 _toText = (_toText == toPlainText) ? toStyledText : toPlainText; 92 }); 93 } 94 95 @override 96 Widget build(BuildContext context) { 97 final List<Widget> lines = _kNameLines 98 .map<Widget>((List<String> nameAndText) => _toText(nameAndText[0], nameAndText[1])) 99 .toList(); 100 101 final List<Widget> children = <Widget>[]; 102 for (Widget line in lines) { 103 children.add(line); 104 if (line != lines.last) 105 children.add(SpeakerSeparator()); 106 } 107 108 return GestureDetector( 109 onTap: _handleTap, 110 child: Container( 111 padding: const EdgeInsets.symmetric(horizontal: 8.0), 112 child: Column( 113 children: children, 114 mainAxisAlignment: MainAxisAlignment.center, 115 crossAxisAlignment: CrossAxisAlignment.start, 116 ), 117 ), 118 ); 119 } 120} 121 122void main() { 123 runApp(MaterialApp( 124 theme: ThemeData.light(), 125 home: Scaffold( 126 appBar: AppBar( 127 title: const Text('Hal and Dave'), 128 ), 129 body: Material( 130 color: Colors.grey.shade50, 131 child: StyledTextDemo(), 132 ), 133 ), 134 )); 135} 136