import 'package:dartboard_resume/dartboard_parser.dart'; import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart'; class DartboardFooter extends StatelessWidget { DartboardFooter({required this.dartboardData, required this.renderNs}); final int renderNs; final DartboardData dartboardData; @override Widget build(Context context) { final double renderTimeMs = renderNs.toDouble() / 1000.0; return Padding( padding: const EdgeInsets.only(top: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Text( 'This resume was generated with DartBoard Resume', style: dartboardData.defaultTextStyle.copyWith(fontSize: 10), ), Text( 'Rendered in ${renderTimeMs.toStringAsFixed(2)}ms', style: dartboardData.defaultTextStyle.copyWith(fontSize: 10), ), ], ), ); } } class DartboardExperienceEntry extends StatelessWidget { DartboardExperienceEntry({required this.dartboardData, required this.exp}); final DartboardData dartboardData; final DartboardExperience exp; @override Widget build(Context context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( padding: const EdgeInsets.only(left: 4.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(exp.title, style: dartboardData.subheaderTextStyle.apply(color: const PdfColorGrey(0.3))), Container(height: 1, width: 60, color: const PdfColorGrey(0.75)), ], ), ), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( exp.range.toString(), style: dartboardData.defaultTextStyle.copyWith(color: const PdfColorGrey(0.42), fontSize: 8.0), ), if (exp.location != null) Text( exp.location!, style: dartboardData.defaultTextStyle.copyWith(color: const PdfColorGrey(0.42), fontSize: 8.0), ), ], ), ], ), SizedBox(height: 6), ...exp.attributes.map( (a) => Padding( child: DartboardTextWithLink( stringSections: a.toTextLinkList(), bulletString: dartboardData.dartboardTheme.bulletPoint, style: dartboardData.defaultTextStyle.apply( color: const PdfColorGrey(0.55), ), ), padding: const EdgeInsets.only(left: 8.0, bottom: 4.0), ), ), SizedBox(height: 12), ], ); } } class DartboardMiscEntry extends StatelessWidget { DartboardMiscEntry({required this.dartboardData, required this.misc}); final DartboardData dartboardData; final DartboardMisc misc; @override Widget build(Context context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ...misc.attributes.map( (a) => Padding( padding: const EdgeInsets.only(left: 8.0, bottom: 4.0), child: DartboardTextWithLink( stringSections: a.toTextLinkList(), bulletString: dartboardData.dartboardTheme.bulletPoint, style: dartboardData.defaultTextStyle.apply( color: const PdfColorGrey(0.55), ), ), ), ), SizedBox(height: 12), ], ); } } class UrlFootnotes { // Factory constructor to return the single instance factory UrlFootnotes() { return _instance; } // Private constructor UrlFootnotes._privateConstructor(); // Static field to hold the single instance of the class static final UrlFootnotes _instance = UrlFootnotes._privateConstructor(); // Field to hold the number int _numUrls = 0; List _urlWidgets = []; TextStyle? _style; // ignore: avoid_setters_without_getters set style(TextStyle style) => _style = style; int add({required String url}) { _numUrls += 1; if (_style == null) { throw Exception('Must provide text style for urls'); } _urlWidgets.add(Footnote(number: _numUrls, style: _style!, url: url)); print(_urlWidgets); return _numUrls; } List get footnotes { final widgets = [..._urlWidgets]; reset(); return widgets; } // Method to reset the number void reset() { _numUrls = 0; _urlWidgets = []; } } // TODO this lays out long text lines on a newline after a uilink rather than soft wrapping it. class DartboardTextWithLink extends StatelessWidget { DartboardTextWithLink({this.bulletString, required this.stringSections, this.style}); final String? bulletString; final List stringSections; final TextStyle? style; @override Widget build(Context context) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(bulletString == null ? '' : '$bulletString ', style: style), SizedBox( width: DartboardTheme.width - DartboardTheme.margin * 2 - 20.0, child: RichText( text: TextSpan( children: [ ...stringSections.map((s) { return TextSpan(text: s.text, style: style); // switch (s.type) { // case DartboardTextType.normal: // return [Text(s.text, style: style)]; // case DartboardTextType.linkText: // final number = UrlFootnotes().add(url: s.url!); // return [ // Text(s.text, style: style), // UrlLink( // destination: s.url!, // child: Padding( // padding: const EdgeInsets.only(bottom: 4.0, left: 2.0, right: 2.0), // child: Text( // number.toString(), // style: style?.copyWith(color: PdfColors.blue, fontSize: 8), // ), // ), // ), // ]; // } }), ...stringSections.map((s) { switch (s.type) { case DartboardTextType.normal: return null; case DartboardTextType.linkText: final number = UrlFootnotes().add(url: s.url!); return TextSpan( text: ' [$number]', style: style?.copyWith(color: PdfColors.blue, fontSize: 8), ); // return UrlLink( // destination: s.url!, // child: Padding( // padding: const EdgeInsets.only(bottom: 4.0, left: 2.0, right: 2.0), // child: Text( // number.toString(), // style: style?.copyWith(color: PdfColors.blue, fontSize: 8), // ), // ), // ); } }).nonNulls, ], ), ), ), ], ); } } class Footnote extends StatelessWidget { Footnote({required this.number, required this.url, required this.style}); final String url; final int number; final TextStyle style; @override Widget build(Context context) { return Row( children: [ UrlLink( destination: url, child: Text( '[$number]', style: style.copyWith(color: PdfColors.blue, fontSize: 8), ), ), Text( ' $url', style: style.copyWith(fontSize: 8), ), ], ); } }